Проверка, имеют ли две таблицы одинаковое содержимое в PostgreSQL

28

Об этом уже спрашивали о переполнении стека , но только для 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

Одним из вариантов является использование 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. Кроме того, это должно работать для любой базы данных, которая поддерживает полные внешние объединения (не только Postgresql).

Я вообще не рекомендую использовать предложение USING, но здесь есть одна ситуация, где я считаю, что это лучший подход.

Приложение 2019-05-03:

Если существует проблема с возможными нулевыми данными (т. Е. Столбец id не обнуляемый, а значение 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 ;
gsiems
источник
Разве это не потерпит неудачу, если val обнуляется?
Амит Гольдштейн
@AmitGoldstein - пустые места будут проблемой. Смотрите мое приложение для одного возможного решения этого.
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делает ключевое слово, - это то, что оно обрабатывает NULLзначения как идентичные, поэтому, если в таблице Aесть строка с, (1,2,NULL)а в таблице Bесть строка с (1,2,NULL), первый запрос не будет отображать эти строки, а второй запрос вернется, 'same'если у двух таблиц нет другой строки.

Если вы хотите считать такие строки разными, вы можете использовать вариант FULL JOINответа gsiems , чтобы получить все (разные) строки:

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 ;

Если все столбцы этих двух таблиц не обнуляются, два подхода дадут одинаковые ответы.

ypercubeᵀᴹ
источник
Не может быть более эффективного метода, не уверен.
ypercubeᵀᴹ
@FaheemMitha вы можете использовать это, чтобы сравнить меньше столбцов, чем все. Просто используйте SELECT <column_list> FROM aвместоTABLE a
ypercubeᵀᴹ
2
EXCEPTЗапрос является Beaut!
Эрвин Брандштеттер,
За исключением запроса сладко!
Шарадов
1

Вам нужно, кроме пункта что-то вроде

SELECT * FROM first_table
EXCEPT
SELECT * FROM second_table

Это возвращает все строки из первой таблицы, которые не во второй таблице

Елен
источник
0

Глядя на связанный код, вы не понимаете:

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

Секретный соус использует unionв отличие от union all. В первом сохраняются только отдельные строки, а во втором - дубликаты ( ссылка ). Другими словами, во вложенных запросах говорится: «Дайте мне все строки и столбцы из EmpDtl1 и, кроме того, из EmpDtl2, которых еще нет в EmpDtl1». Количество этого подзапроса будет равно количеству EmpDtl1 в том и только в том случае, если EmpDtl2 не вносит каких-либо строк в результат, т.е. две таблицы идентичны.

Кроме того, дамп таблицы в последовательности ключей в два текстовых файла и использовать свой инструмент сравнения по вашему выбору.

Майкл Грин
источник
3
Это не обнаружит случай , когда EmpDtl2имеет меньше строк , чем EmpDtl1и все существующие строки существуют в EmpDtl1.
a_horse_with_no_name