マスターローと一致しない行を識別します
-
22-10-2019 - |
質問
さまざまなサーバー上のさまざまなデータベースのテーブルの束をマスターレコードと比較しています。どのサーバーで識別されたかを知る必要があります locationID
, 、メンテナンスが必要になる可能性があるため、一致しない行があります。
シンプルなことがあります EXCEPT
各行が各サーバーからの構成であるテーブルを比較するクエリ。 table1
すべての構成プラスを備えたサーバーごとに1つの行があります locationID
これは、どのサーバーであるかを教えてくれる列です。これらすべてをaと比較します table1_master
適切な設定があるテーブルですが、 locationID
一致しないので。
以下の簡単なクエリ:
SELECT everything, but, locationID
FROM table1
EXCEPT
SELECT everything, but, locationID
FROM table1_master
だけあります 1 マスターロウ私はすべてのサーバーを比較し、それを選択しません locationID
ここ。
これは、私が比較している行の例です。それぞれにプライマリキー、単一の列があります varchar
そして、それの巨大なリストは数十の列です。すべての列を比較したいです それ外 LocationIdですが、行を識別するにはLocationIDが必要です。
LocationID setting setting setting setting
CS02 C Y Y Y Y
CS03 C Y Y Y Y
CS06 C Y N Y Y
この例では、CS02が私のマスターレコードであると言うため、すべての設定はCS02とCS03で同じであるため、それらの行は表示されませんが、CS06はそうです。しかし、私の中で EXCEPT
クエリ、私は実際にLocationIDをキャッチしていないので、実際にどの行が返されたかわかりません。
これにより、必要な行が返されますが、そうではありません locationID
, 、だから、どの行が間違っているのかわかりません。含める方法はありますか? locationID
一致する行をキックアウトしながら設定された結果では?
私が考えていた解決策は、 table1_master
テーブル、それぞれ locationID
表現されていますが、それらはすべてそれ以外のデータを持っています。私の EXCLUDE
その後、クエリはを返します locationID
そして私の情報ですが、それはそれをするための最良の方法ですか?
解決
また、すべての列名を手動で構築することなく、動的SQLでこれを行うこともできます。
DECLARE @sql NVARCHAR(MAX), @c1 NVARCHAR(MAX), @c2 NVARCHAR(MAX);
SELECT @c1 = N'', @c2 = N'';
SELECT
@c1 = @c1 + ',' + QUOTENAME(name),
@c2 = @c2 + ' AND m.' + QUOTENAME(name) + ' = s.' + QUOTENAME(name)
FROM sys.columns
WHERE name <> 'LocationID'
AND [object_id] = OBJECT_ID('dbo.table1');
SET @sql = ';WITH s AS (
SELECT ' + STUFF(@c1, 1, 1, '') + ' FROM dbo.table1
EXCEPT
SELECT ' + STUFF(@c1, 1, 1, '') + ' FROM dbo.table1_master
)
SELECT m.LocationID
FROM s INNER JOIN dbo.table1 AS m ON 1 = 1
' + @c2;
SELECT @sql;
--EXEC sp_executesql @sql;
このクエリの出力をそのままにして、どこかにクエリを保存するか、コメントすることができます。 SELECT
そして、除外 EXEC
永続的な動的SQLとして残します - この場合、2つのテーブルの列の変更に自動的に適応します。
別のアイデア(LocationIDがユニークであると仮定して) - そして、私には、マスターローを含めて、違う列をすばやく見つけることができるようにすることができます。
;WITH c AS
(
SELECT t.LocationID, m.setting1, m.setting2, ...
FROM dbo.table1 AS t CROSS JOIN dbo.table1_master AS m
)
SELECT DISTINCT src = '> master', setting1, setting2, ...
FROM c
UNION ALL
(
SELECT RTRIM(LocationID), setting1, setting2, ...
FROM dbo.table1
EXCEPT
SELECT RTRIM(LocationID), setting1, setting2, ...
FROM c
)
ORDER BY src;
このバージョンは少し安いです(ほとんどの場合 DISTINCT
マスターテーブルに対して、すべての列をもう一度指定する必要があるという犠牲を払って、これも上記のように自動化できます):
;WITH m AS
(
SELECT setting1, setting2, ...
FROM dbo.table1_master
),
c AS
(
SELECT src = RTRIM(t.LocationID), m.setting1, m.setting2, ...
FROM dbo.table1 AS t CROSS JOIN m
)
SELECT src = '> master', setting1, setting2, ...
FROM m
UNION ALL
(
SELECT RTRIM(LocationID), setting1, setting2, ...
FROM dbo.table1
EXCEPT
SELECT src, setting1, setting2, ...
FROM c
)
ORDER BY src;
しかし、これらのオプションはすべて、レイチェルのシンプルよりも悪い計画を持つパフォーマーの貧しい人です LEFT JOIN
. 。私は使用のテーマに固執しようとしました EXCEPT
パフォーマンスよりも構文に関するものですが。
重要なポイントは、列のカウントが高すぎて手動で処理できない場合、上記のダイナミックSQLアプローチを使用して使用する任意のクエリを構築できることです。毎回生成されます。動的SQLを使用してRachelのクエリを生成するには、変更する必要はあまりありません。
DECLARE @sql NVARCHAR(MAX), @and NVARCHAR(MAX), @anycol NVARCHAR(128);
SELECT @sql = N'', @and = N'';
SELECT @and = @and + ' AND t.' + QUOTENAME(name) + ' = m.' + QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.table1_master');
SELECT TOP (1) @anycol = QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.table1_master')
ORDER BY name;
SET @sql = 'SELECT locationID
FROM dbo.table1 AS t
LEFT OUTER JOIN dbo.table1_master AS m ON 1 = 1'
+ @and + ' WHERE m.' + @anycol + ' IS NULL;';
SELECT @sql;
--EXEC sp_executesql @sql;
他のヒント
私がお勧めします:
- 作成
Hash
の線に沿って定義を持つ永続的な計算列であるフィールドHASHBYTES('SHA1', Field1 + Field2 + Field3...)
- それを比較します
HASH
あなたの「マスター」から他のレコードへの価値 - 一致しない行からすべての実際の値を表示します
何かのようなもの
SELECT *
FROM Table1
WHERE HashField <> (SELECT Hashfield FROM Table1_Master)
すべての列の2つのテーブルを結合する(またはwhereステートメントを使用)し、2番目のテーブルに存在しないアイテムを選択するだけで何が問題になっていますか?
SELECT locationID
FROM table1
LEFT OUTER JOIN table1_master
ON table1.a = table1_master.a
AND table1.b = table1_master.b
AND table1.c = table1_master.c
WHERE table1_master.a is null
きれいではないかもしれませんが、うまくいくはずです