Prüfen, ob zwei Tabellen in PostgreSQL identischen Inhalt haben


28

Dies wurde bereits bei Stack Overflow abgefragt , jedoch nur für MySQL. Ich benutze PostgreSQL. Leider (und überraschenderweise) scheint PostgreSQL so etwas nicht zu haben CHECKSUM table.

Eine PostgreSQL-Lösung wäre in Ordnung, aber eine generische wäre besser. Ich habe http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-data gefunden , aber ich verstehe die verwendete Logik nicht.

Hintergrund: Ich habe einige datenbankgenerierende Codes neu geschrieben, daher muss ich prüfen, ob der alte und der neue Code identische Ergebnisse liefern.


3
Sie können EXCEPT
Folgendes

pg_comparator führt einen effizienten Vergleich und eine Synchronisation des Tabelleninhalts durch
natmaka

@natmaka Sollte dies eine separate Antwort sein?
Faheem Mitha

Antworten:


24

Eine Möglichkeit besteht darin, einen FULL OUTER JOIN zwischen den beiden Tabellen in der folgenden Form zu verwenden:

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 ;

Beispielsweise:

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 ;

Gibt eine Anzahl von 2 zurück, wobei:

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 ;

gibt die erhoffte Anzahl von 0 zurück.

Das, was ich an dieser Methode mag, ist, dass sie bei Verwendung von EXISTS jede Tabelle nur einmal und jede Tabelle nur zweimal lesen muss. Darüber hinaus sollte dies für jede Datenbank funktionieren, die vollständige äußere Verknüpfungen unterstützt (nicht nur Postgresql).

Ich rate generell von der Verwendung der USING-Klausel ab, aber hier ist eine Situation, in der ich glaube, dass dies der bessere Ansatz ist.

Nachtrag 2019-05-03:

Wenn es ein Problem mit möglichen Nulldaten gibt (dh die ID-Spalte ist nicht nullfähig, der Wert jedoch), können Sie Folgendes versuchen:

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 ;

Würde das nicht scheitern, wenn val nullbar ist?
Amit Goldstein

@AmitGoldstein - Nullen wären ein Problem. In meinem Anhang finden Sie eine mögliche Lösung dafür.
gsiems

30

Sie können den EXCEPTOperator verwenden. Wenn die Tabellen beispielsweise eine identische Struktur haben, werden im Folgenden alle Zeilen zurückgegeben, die sich in einer Tabelle, jedoch nicht in der anderen befinden (also 0 Zeilen, wenn die Tabellen identische Daten enthalten):

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

Oder mit EXISTS, um nur einen Booleschen Wert oder eine Zeichenfolge mit einem der beiden möglichen Ergebnisse zurückzugeben:

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

Getestet bei SQLfiddle


Auch nicht die, EXCEPTdie Duplikate entfernt (das sollte keine Sorge sein, wenn Ihre Tabellen einige PRIMARY KEYoder habenUNIQUE Einschränkungen haben, aber es kann sein, wenn Sie Ergebnisse von willkürlichen Abfragen vergleichen, die möglicherweise doppelte Zeilen erzeugen können).

Das EXCEPTSchlüsselwort behandelt NULLWerte als identisch. Wenn also eine Tabelle Aeine Zeile mit (1,2,NULL)und eine Tabelle Beine Zeile mit enthält (1,2,NULL), werden diese Zeilen in der ersten Abfrage nicht angezeigt, und die zweite Abfrage wird zurückgegeben, 'same'wenn die beiden Tabellen keine andere Zeile enthalten.

Wenn Sie solche Zeilen als unterschiedlich zählen möchten, können Sie eine Variation der FULL JOINAntwort von gsiems verwenden , um alle (verschiedenen) Zeilen abzurufen :

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

und um eine Ja / Nein-Antwort zu erhalten:

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 ;

Wenn nicht alle Spalten der beiden Tabellen nullwertfähig sind, geben die beiden Ansätze identische Antworten.


Es könnte eine effizientere Methode geben, nicht sicher.
Ypercubeᵀᴹ

@FaheemMitha Mit dieser Option können Sie weniger Spalten als alle vergleichen. Verwenden Sie SELECT <column_list> FROM astattTABLE a
ypercubeᵀᴹ 29.07.14

2
Die EXCEPTAbfrage ist eine schöne!
Erwin Brandstetter

AUSGENOMMEN Abfrage ist süß!
Sharadov

1

Du brauchst so etwas wie Except Klausel

SELECT * FROM first_table
EXCEPT
SELECT * FROM second_table

Dadurch werden alle Zeilen aus der ersten Tabelle zurückgegeben, die nicht in der zweiten Tabelle enthalten sind


0

Wenn Sie den verknüpften Code betrachten, verstehen Sie nicht:

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

Die geheime Sauce wird unionim Gegensatz dazu verwendet union all. Ersteres behält nur einzelne Zeilen bei, während letzteres Duplikate ( Verweise ) enthält. Mit anderen Worten, die verschachtelten Abfragen sagen "gib mir alle Zeilen und Spalten von EmpDtl1 und zusätzlich die von EmpDtl2, die noch nicht in EmpDtl1 sind". Die Anzahl dieser Unterabfrage entspricht genau dann der Anzahl von EmpDtl1, wenn EmpDtl2 keine Zeilen zum Ergebnis beiträgt, dh die beiden Tabellen identisch sind.

Alternativ können Sie die Tabellen in Schlüsselsequenz in zwei Textdateien ablegen und das Vergleichstool Ihrer Wahl verwenden.


3
Dies wird nicht der Fall erkennen , wann EmpDtl2hat weniger Zeilen als EmpDtl1und alle vorhandenen Zeilen existieren in EmpDtl1.
a_horse_with_no_name
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.