PostgreSQLで2つのテーブルに同じコンテンツがあるかどうかを確認する


28

これはすでにStack Overflow要求されていますが、MySQLのみです。PostgreSQLを使用しています。残念ながら(そして驚くべきことに)PostgreSQLにはのようなものはないようですCHECKSUM table

PostgreSQLのソリューションは問題ありませんが、一般的なソリューションの方が優れています。http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-dataを見つけましたが、使用されているロジックがわかりません。

背景:データベースを生成するコードを書き直したので、古いコードと新しいコードが同じ結果を生成するかどうかを確認する必要があります。


3
あなたは使用することができますEXCEPT:この質問をチェック、SQLで二つの大きなデータセットを比較するための効率的な方法
ypercubeᵀᴹ

pg_comparatorは効率的なテーブルコンテンツの比較と同期を行います
natmaka

@natmakaこれは別の答えでしょうか?
ファヒムミサ

回答:


24

1つのオプションは、次の形式で2つのテーブル間でFULL OUTER JOINを使用することです。

SELECT count (1)
    FROM table_a a
    FULL OUTER JOIN table_b b 
        USING (<list of columns to compare>)
    WHERE a.id IS NULL
        OR b.id IS NULL ;

例えば:

CREATE TABLE a (id int, val text);
INSERT INTO a VALUES (1, 'foo'), (2, 'bar');

CREATE TABLE b (id int, val text);
INSERT INTO b VALUES (1, 'foo'), (3, 'bar');

SELECT count (1)
    FROM a
    FULL OUTER JOIN b 
        USING (id, val)
    WHERE a.id IS NULL
        OR b.id IS NULL ;

一方、カウント2を返します。

CREATE TABLE a (id int, val text);
INSERT INTO a VALUES (1, 'foo'), (2, 'bar');

CREATE TABLE b (id int, val text);
INSERT INTO b VALUES (1, 'foo'), (2, 'bar');

SELECT count (1)
    FROM a
    FULL OUTER JOIN b 
        USING (id, val)
    WHERE a.id IS NULL
        OR b.id IS NULL ;

期待される0のカウントを返します。

このメソッドで気に入っているのは、EXISTSを使用する場合に各テーブルを2回読み取るのに対して、各テーブルを1回読み取るだけで済むことです。さらに、これは(Postgresqlだけでなく)完全外部結合をサポートするすべてのデータベースで機能するはずです。

私は一般的にUSING句の使用を推奨していませんが、これがより良いアプローチであると信じる状況の1つです。

補遺2019-05-03:

nullデータの可能性に問題がある場合(つまり、id列はnull可能ではありませんが、valはそうです)、次を試すことができます。

SELECT count (1)
    FROM a
    FULL OUTER JOIN b
        ON ( a.id = b.id
            AND a.val IS NOT DISTINCT FROM b.val )
    WHERE a.id IS NULL
        OR b.id IS NULL ;

valがNULL可能であればこれは失敗しませんか?
アミットゴールドスタイン

@AmitGoldstein-nullが問題になります。その解決策の1つについては、補遺を参照してください。
gsiems

30

EXCEPT演算子を使用できます。たとえば、テーブルの構造が同一である場合、以下は一方のテーブルに含まれるすべての行を返しますが、もう一方のテーブルには含まれません(テーブルのデータが同一の場合は0行)。

(TABLE a EXCEPT TABLE b)
UNION ALL
(TABLE b EXCEPT TABLE a) ;

またはEXISTS、2つの可能な結果のいずれかでブール値または文字列のみを返す場合:

SELECT CASE WHEN EXISTS (TABLE a EXCEPT TABLE b)
              OR EXISTS (TABLE b EXCEPT TABLE a)
            THEN 'different'
            ELSE 'same'
       END AS result ;

SQLfiddleでテスト済み


また、ではないことEXCEPT重複を(あなたのテーブルがいくつか持っている場合には心配すべきではない削除PRIMARY KEYまたはUNIQUE制約がいますが、潜在的に重複行を生成することができ、任意のクエリの結果を比較している場合、それは可能性があります)。

EXCEPTキーワードが行うもう1つのことは、NULL値を同一として扱うため、テーブルAに行が(1,2,NULL)あり、テーブルBに行がある(1,2,NULL)場合、最初のクエリはこれらの行を表示せず'same'、2つのテーブルに他の行がない場合に2 番目のクエリが返されます。

そのような行を異なるものとしてカウントする場合は、gsiemsのFULL JOIN回答のバリエーションを使用して、すべての(異なる)行を取得できます。

SELECT *
FROM a NATURAL FULL JOIN b
WHERE a.some_not_null_column IS NULL 
   OR b.some_not_null_column IS NULL ;

そして、はい/いいえの答えを得るには:

SELECT CASE WHEN EXISTS
            ( SELECT *
              FROM a NATURAL FULL JOIN b
              WHERE a.some_not_null_column IS NULL 
                 OR b.some_not_null_column IS NULL
            )
            THEN 'different'
            ELSE 'same'
       END AS result ;

2つのテーブルのすべての列がNULL可能でない場合、2つのアプローチは同じ答えを提供します。


もっと効率的な方法があるかもしれませんが、確かではありません。
ypercubeᵀᴹ

@FaheemMithaを使用すると、すべてよりも少ない列を比較できます。ただ、使うSELECT <column_list> FROM a代わりにTABLE a
ypercubeᵀᴹ

2
EXCEPTクエリはbeautです!
アーウィンブランドステッター14

EXCEPTクエリは甘い!
シャラドフ

1

Except句が必要です

SELECT * FROM first_table
EXCEPT
SELECT * FROM second_table

これは、2番目のテーブルにない最初のテーブルのすべての行を返します


0

理解できないリンクされたコードを見ると:

select count(*) from
(
select * From EmpDtl1
union
select * From EmpDtl2
)

秘密のソースを使用するのunionとは対照的にunion all。前者は個別の行のみを保持し、後者は重複を保持します(reference)。言い換えれば、ネストされたクエリは、「EmpDtl1からすべての行と列を取得し、さらにEmpDtl1にまだないEmpDtl2からも取得します」と言います。EmpDtl2が結果に行を提供しない場合、つまり2つのテーブルが同一である場合にのみ、このサブクエリのカウントはEmpDtl1のカウントと等しくなります。

または、キーシーケンスでテーブルを2つのテキストファイルにダンプし、選択した比較ツールを使用します。


3
これは、行数EmpDtl2未満EmpDtl1、すべての既存の行がに存在する場合を検出しませんEmpDtl1
a_horse_with_no_name
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.