Verifica se due tabelle hanno contenuto identico in PostgreSQL


28

Questo è già stato chiesto su Stack Overflow , ma solo per MySQL. Sto usando PostgreSQL. Sfortunatamente (e sorprendentemente) PostgreSQL non sembra avere qualcosa di simile CHECKSUM table.

Una soluzione PostgreSQL andrebbe bene, ma una soluzione generica sarebbe migliore. Ho trovato http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-data , ma non capisco la logica utilizzata.

Contesto: ho riscritto un po 'di codice per la generazione di database, quindi devo verificare se il vecchio e il nuovo codice producono risultati identici.


3

pg_comparator esegue un confronto e una sincronizzazione efficienti del contenuto della tabella
natmaka,

@natmaka Questa dovrebbe essere una risposta separata?
Faheem Mitha,

Risposte:


24

Un'opzione è quella di utilizzare un JOU COMPLETO ESTERNO tra le due tabelle nel seguente formato:

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 ;

Per esempio:

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 ;

Restituirà un conteggio di 2, mentre:

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 ;

restituisce il conteggio sperato di 0.

La cosa che mi piace di questo metodo è che deve solo leggere ogni tabella una volta rispetto a ogni tabella due volte quando si utilizza EXISTS. Inoltre, questo dovrebbe funzionare per qualsiasi database che supporti i join esterni completi (non solo Postgresql).

In genere scoraggio l'uso della clausola USING ma qui c'è una situazione in cui credo che sia l'approccio migliore.

Addendum 2019-05-03:

Se si verifica un problema con possibili dati null, (ovvero la colonna id non è nulla ma il valore val è), è possibile provare quanto segue:

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 ;

Non fallirebbe se val fosse nullable?
Amit Goldstein,

@AmitGoldstein - i null sarebbero un problema. Vedi il mio addendum per una possibile soluzione a questo.
gsiems,

30

Puoi usare l' EXCEPToperatore. Ad esempio, se le tabelle hanno una struttura identica, quanto segue restituirà tutte le righe che si trovano in una tabella ma non nell'altra (quindi 0 righe se le tabelle hanno dati identici):

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

Oppure con EXISTSper restituire solo un valore booleano o una stringa con uno dei 2 risultati possibili:

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

Testato su SQLfiddle


Inoltre, ciò non EXCEPTrimuove i duplicati (ciò non dovrebbe costituire un problema se le tabelle presentano alcuni PRIMARY KEYo UNIQUEvincoli, ma potrebbe essere se si stanno confrontando i risultati di query arbitrarie che possono potenzialmente produrre righe duplicate).

Un'altra cosa che EXCEPTfa la parola chiave è che tratta i NULLvalori come identici, quindi se la tabella Aha una riga con (1,2,NULL)e la tabella Bha una riga con (1,2,NULL), la prima query non mostrerà queste righe e la seconda query restituirà 'same'se le due tabelle non hanno altra riga.

Se vuoi contare tali righe come diverse, puoi usare una variazione sulla FULL JOINrisposta di gsiems , per ottenere tutte le (diverse) righe:

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

e per ottenere una risposta sì / no:

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 ;

Se tutte le colonne delle due tabelle non sono annullabili, i due approcci daranno risposte identiche.


Potrebbe esserci un metodo più efficiente, non sono sicuro.
ypercubeᵀᴹ

@FaheemMitha puoi usarlo per confrontare meno colonne di tutte. Basta usare SELECT <column_list> FROM ainvece diTABLE a
ypercubeᵀᴹ

2
La EXCEPTquery è bellissima!
Erwin Brandstetter,

TRANNE la query è dolce!
sharadov,

1

È necessario tranne la clausola Something like

SELECT * FROM first_table
EXCEPT
SELECT * FROM second_table

Ciò restituisce tutte le righe dalla prima tabella che non sono nella seconda tabella


0

Guardando il codice collegato non capisci:

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

La salsa segreta sta usando unional contrario union all. Il primo conserva solo righe distinte mentre il secondo mantiene duplicati ( riferimento ). In altre parole, le query nidificate dicono "dammi tutte le righe e le colonne di EmpDtl1 e inoltre quelle di EmpDtl2 che non sono già in EmpDtl1". Il conteggio di questa sottoquery sarà uguale al conteggio di EmpDtl1 se e solo se EmpDtl2 non fornisce alcuna riga al risultato, ovvero le due tabelle sono identiche.

In alternativa, scaricare le tabelle in sequenza di tasti su due file di testo e utilizzare lo strumento di confronto preferito.


3
Questo non rileverà il caso in cui EmpDtl2ha meno righe di EmpDtl1e tutte le righe esistenti esistono EmpDtl1.
a_horse_with_no_name
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.