Перевірка наявності двох таблиць однакового вмісту в PostgreSQL


28

Про це вже задавались у Stack Overflow , але лише для MySQL. Я використовую PostgreSQL. На жаль (і що дивно) у PostgreSQL не здається щось подібне CHECKSUM table.

Рішення PostgreSQL було б добре, але загальне було б краще. Я знайшов http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-data , але я не розумію використовуваної логіки.

Передумови: я переписав деякий код генерування бази даних, тому мені потрібно перевірити, чи дає старий і новий код однакові результати.



pg_comparator робить ефективне порівняння та синхронізацію вмісту таблиць
natmaka

@natmaka Це має бути окрема відповідь?
Faheem Mitha

Відповіді:


24

Один із варіантів полягає у використанні ПОВНОГО ВИХІДНОГО ПРИЄДНАННЯ між двома таблицями у такій формі:

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 ;

Хіба це не вдасться, якщо 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'якщо в двох таблицях немає іншого рядка.

Якщо ви хочете вважати такі рядки різними, ви можете скористатися варіантом відповіді 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 ;

Якщо всі стовпці двох таблиць не є нульовими, два підходи дадуть однакові відповіді.


Може бути якийсь більш ефективний метод, не впевнений.
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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.