質問

さまざまなサーバー上のさまざまなデータベースのテーブルの束をマスターレコードと比較しています。どのサーバーで識別されたかを知る必要があります 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

きれいではないかもしれませんが、うまくいくはずです

ライセンス: CC-BY-SA帰属
所属していません dba.stackexchange
scroll top