質問

同一のデータを持っている/返すことになっている2つの異なるテーブル/クエリがあると想像してください。あなたはこれを確認したいです。すべての列を比較して、以下の例のように、各テーブルから比類のない行を表示する簡単な方法は何ですか?テーブルには30の列があると仮定し、その多くは気付かれません。

PKがない場合、またはPKごとに重複する可能性がある場合、PK列のみに結合するだけでは十分ではなく、ヌルを適切に処理する30の結合条件で完全な結合を行う必要があることに加えて、条件がある場合の厄介な場合は災害になります。一致した行を除外します。

通常、問題が最悪であり、PKが論理的に利用可能である可能性が非常に低いのは、私が未使用または不明確なデータに対して新しいクエリを書いているときです。私は問題を解決してから結果を比較するために2つの異なる方法を調理し、その結果を比較します。これは、私が知らなかったデータの特別なケースを強調する違いです。

結果は次のようになる必要があります:

Which   Col1   Col2   Col3   ... Col30
------  ------ ------ ------     ------
TableA  Cat    27     86               -- mismatch
TableB  Cat    27     105              -- mismatch
TableB  Cat    27     87               -- mismatch 2
TableA  Cat    128    92               -- no corresponding row
TableB  Lizard 83     NULL             -- no corresponding row

もしも [Col1, Col2] たまたま複合キーであり、最終結果で注文すると、AとBが同じである必要がある1つの行が異なり、それぞれが他の行にない1つの行があることが簡単に確認できます。

上記の例では、最初の行を2回見ることは望ましくありません。

サンプルテーブルとデータをセットアップするDDLとDMLは次のとおりです。

CREATE TABLE dbo.TableA (
   Col1 varchar(10),
   Col2 int,
   Col3 int,
   Col4 varchar(10),
   Col5 varchar(10),
   Col6 varchar(10),
   Col7 varchar(10),
   Col8 varchar(10),
   Col9 varchar(10),
   Col10 varchar(10),
   Col11 varchar(10),
   Col12 varchar(10),
   Col13 varchar(10),
   Col14 varchar(10),
   Col15 varchar(10),
   Col16 varchar(10),
   Col17 varchar(10),
   Col18 varchar(10),
   Col19 varchar(10),
   Col20 varchar(10),
   Col21 varchar(10),
   Col22 varchar(10),
   Col23 varchar(10),
   Col24 varchar(10),
   Col25 varchar(10),
   Col26 varchar(10),
   Col27 varchar(10),
   Col28 varchar(10),
   Col29 varchar(10),
   Col30 varchar(10)
);

CREATE TABLE dbo.TableB (
   Col1 varchar(10),
   Col2 int,
   Col3 int,
   Col4 varchar(10),
   Col5 varchar(10),
   Col6 varchar(10),
   Col7 varchar(10),
   Col8 varchar(10),
   Col9 varchar(10),
   Col10 varchar(10),
   Col11 varchar(10),
   Col12 varchar(10),
   Col13 varchar(10),
   Col14 varchar(10),
   Col15 varchar(10),
   Col16 varchar(10),
   Col17 varchar(10),
   Col18 varchar(10),
   Col19 varchar(10),
   Col20 varchar(10),
   Col21 varchar(10),
   Col22 varchar(10),
   Col23 varchar(10),
   Col24 varchar(10),
   Col25 varchar(10),
   Col26 varchar(10),
   Col27 varchar(10),
   Col28 varchar(10),
   Col29 varchar(10),
   Col30 varchar(10)
);

INSERT dbo.TableA (Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16, Col17, Col18, Col19, Col20, Col21, Col22, Col23, Col24, Col25, Col26, Col27, Col28, Col29, Col30)
VALUES
   ('Cat', 27, 86, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Cat', 128, 92, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Porcupine', NULL, 42, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Tapir', NULL, NULL, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0')
;

INSERT dbo.TableB (Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16, Col17, Col18, Col19, Col20, Col21, Col22, Col23, Col24, Col25, Col26, Col27, Col28, Col29, Col30)
VALUES
   ('Cat', 27, 105, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Cat', 27, 87, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Lizard', 83, NULL, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Porcupine', NULL, 42, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Tapir', NULL, NULL, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0');
役に立ちましたか?

解決

aの30の結合条件は必要ありません FULL OUTER JOIN ここ。

PKの完全な外側結合だけで、少なくとも1つの違いがある行を保持できます WHERE EXISTS (SELECT A.* EXCEPT SELECT B.*) そして使用します CROSS APPLY (SELECT A.* UNION ALL SELECT B.*) の両側を解放する JOINED行は個々の行に入ります。

WITH TableA(Col1, Col2, Col3) 
     AS (SELECT 'Dog',1,1     UNION ALL 
         SELECT 'Cat',27,86   UNION ALL 
         SELECT 'Cat',128,92), 
     TableB(Col1, Col2, Col3) 
     AS (SELECT 'Dog',1,1     UNION ALL 
         SELECT 'Cat',27,105  UNION ALL 
         SELECT 'Lizard',83,NULL) 
SELECT CA.*
FROM   TableA A 
       FULL OUTER JOIN TableB B 
         ON A.Col1 = B.Col1 
            AND A.Col2 = B.Col2 
/*Unpivot the joined rows*/
CROSS APPLY (SELECT 'TableA' AS what, A.* UNION ALL
             SELECT 'TableB' AS what, B.*) AS CA     
/*Exclude identical rows*/
WHERE  EXISTS (SELECT A.* 
               EXCEPT 
               SELECT B.*) 
/*Discard NULL extended row*/
AND CA.Col1 IS NOT NULL      
ORDER BY CA.Col1, CA.Col2

与えます

what   Col1   Col2        Col3
------ ------ ----------- -----------
TableA Cat    27          86
TableB Cat    27          105
TableA Cat    128         92
TableB Lizard 83          NULL

または、移動したゴールポストを扱うバージョン。

SELECT DISTINCT CA.*
FROM   TableA A 
       FULL OUTER JOIN TableB B 
         ON EXISTS (SELECT A.*  INTERSECT  SELECT B.*) 
CROSS APPLY (SELECT 'TableA' AS what, A.* UNION ALL
             SELECT 'TableB' AS what, B.*) AS CA     
WHERE NOT EXISTS (SELECT A.*  INTERSECT  SELECT B.*) 
AND CA.Col1 IS NOT NULL
ORDER BY CA.Col1, CA.Col2  

多くの列のあるテーブルの場合、異なる特定の列を識別することは依然として困難です。そのためには、以下を使用できます。

(ただし、比較的小さなテーブルでは、この方法は適切なパフォーマンスを持たない可能性があります)

SELECT t1.primary_key,
       y1.c,
       y1.v,
       y2.v
FROM   t1
       JOIN t2
         ON t1.primary_key = t2.primary_key
       CROSS APPLY (SELECT t1.*
                    FOR xml path('row'), elements xsinil, type) x1(x)
       CROSS APPLY (SELECT t2.*
                    FOR xml path('row'), elements xsinil, type) x2(x)
       CROSS APPLY (SELECT n.n.value('local-name(.)', 'sysname'),
                           n.n.value('.', 'nvarchar(max)')
                    FROM   x1.x.nodes('row/*') AS n(n)) y1(c, v)
       CROSS APPLY (SELECT n.n.value('local-name(.)', 'sysname'),
                           n.n.value('.', 'nvarchar(max)')
                    FROM   x2.x.nodes('row/*') AS n(n)) y2(c, v)
WHERE  y1.c = y2.c
       AND EXISTS(SELECT y1.v
                  EXCEPT
                  SELECT y2.v) 

他のヒント

これは、除外および/または交差することを使用して処理できます。http://msdn.microsoft.com/en-us/library/ms188055.aspx

最初に、表2にないTable1にあるすべてのレコードを見つけてから、表2にない表2にあるすべてのレコードを見つけます。

SELECT * FROM table1
EXCEPT
SELECT * FROM table2

UNION

SELECT * FROM table2
EXCEPT
SELECT * FROM table1

間違いなくこれを行うためのより効率的な方法がありますが、それは私の頭の上部から最初の「迅速で汚れた」解です。また、 *ワイルドカードを使用することはお勧めしませんが、ここでは簡潔に適しています。

または、交差する演算子を使用して、そこからすべての結果を除外することもできます。

Data Compareなどのサードパーティのツールで簡単に達成できます。単体テストのストアドプロシージャのコンテキストでは、いくつかのC#コードを書きました。

これは、古い記事から引用されている私たちが使用しているC#コードです。これらの抜け穴を閉じます - ストアドプロシージャをテストします

   internal static class DataSetComparer
   {
      internal static bool Compare(DataSet one, DataSet two)
      {
         if(one.Tables.Count != two.Tables.Count)
            return false;

         for(int i = 0; i < one.Tables.Count; i++)
            if(!CompareTables(one.Tables[i], two.Tables[i]))
               return false;

         return true;
        }

      private static bool CompareTables(DataTable one, DataTable two)
      {
         if(one.Rows.Count != two.Rows.Count)
            return false;

         for(int i = 0; i < one.Rows.Count; i++)
            if(!CompareRows(one.Rows[i], two.Rows[i]))
               return false;

         return true;
      }

      private static bool CompareRows(DataRow one, DataRow two)
      {
         if(one.ItemArray.Length != two.ItemArray.Length)
            return false;

         for(int i = 0; i < one.ItemArray.Length; i++)
            if(!CompareItems(one.ItemArray[i], two.ItemArray[i]))
               return false;

         return true;
      }

      private static bool CompareItems(object value1, object value2)
      {
         if(value1.GetType() != value2.GetType())
            return false;

         if(value1 is DBNull)
            return true;

         if(value1 is DateTime)
            return ((DateTime) value1).CompareTo((DateTime) value2)
                                                              == 0;

         if(value1 is byte[])
         {
            if(((byte[]) value1).Length != ((byte[]) value2).Length)
               return false;

            for(int i = 0; i < ((byte[]) value1).Length; i++)
               if(((byte[]) value1)[i] != ((byte[]) value2)[i])
                  return false;

            return true;
         }

         return value1.ToString().Equals(value2.ToString());
      }
   }

これが求められたものを示す方法です:

SELECT
   Which = 'TableA',
   *
FROM (
   SELECT * FROM dbo.TableA
   EXCEPT
   SELECT * FROM dbo.TableB
) X
UNION ALL
SELECT
   'TableB',
   *
FROM (
   SELECT * FROM dbo.TableB
   EXCEPT
   SELECT * FROM dbo.TableA
) X
ORDER BY
   Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16, Col17, Col18, Col19, Col20, Col21, Col22, Col23, Col24, Col25, Col26, Col27, Col28, Col29, Col30
;
ライセンス: CC-BY-SA帰属
所属していません dba.stackexchange
scroll top