Kiểm tra xem hai bảng có nội dung giống hệt nhau trong PostgreSQL không


28

Điều này đã được yêu cầu trên Stack Overflow , nhưng chỉ dành cho MySQL. Tôi đang sử dụng PostgreSQL. Thật không may (và đáng ngạc nhiên) PostgreSQL dường như không có cái gì đó như thế CHECKSUM table.

Một giải pháp PostgreSQL sẽ tốt, nhưng một giải pháp chung sẽ tốt hơn. Tôi tìm thấy http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-data , nhưng tôi không hiểu logic được sử dụng.

Bối cảnh: Tôi đã viết lại một số mã tạo cơ sở dữ liệu, vì vậy tôi cần kiểm tra xem mã cũ và mã mới có tạo ra kết quả giống hệt nhau không.


3
Bạn có thể sử dụng EXCEPT, kiểm tra câu hỏi này: Một cách hiệu quả để so sánh hai tập dữ liệu lớn trong SQL
ypercubeᵀᴹ

pg_comparator thực hiện so sánh và đồng bộ hóa nội dung bảng hiệu quả
natmaka

@natmaka Đây có nên là một câu trả lời riêng biệt?
Faheem Mitha

Câu trả lời:


24

Một tùy chọn là sử dụng FULL OUTER THAM GIA giữa hai bảng theo mẫu sau:

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 ;

Ví dụ:

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 ;

Sẽ trả về số lượng là 2, trong khi:

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 ;

trả về hy vọng cho số 0.

Điều tôi thích ở phương pháp này là nó chỉ cần đọc mỗi bảng một lần so với đọc mỗi bảng hai lần khi sử dụng EXISTS. Ngoài ra, điều này sẽ hoạt động cho bất kỳ cơ sở dữ liệu nào hỗ trợ các phép nối ngoài đầy đủ (không chỉ Postgresql).

Tôi thường không khuyến khích sử dụng điều khoản SỬ DỤNG nhưng đây là một tình huống mà tôi tin rằng đó là cách tiếp cận tốt hơn.

Phụ lục 2019-05-03:

Nếu có vấn đề với dữ liệu null có thể xảy ra, (tức là cột id không thể rỗng nhưng giá trị là) thì bạn có thể thử như sau:

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 ;

Điều này sẽ không thất bại nếu val là nullable?
Amit Goldstein

@AmitGoldstein - nulls sẽ là một vấn đề. Xem phụ lục của tôi cho một giải pháp có thể cho điều đó.
gsiems

30

Bạn có thể sử dụng EXCEPTtoán tử. Ví dụ: nếu các bảng có cấu trúc giống hệt nhau, các bảng sau sẽ trả về tất cả các hàng nằm trong một bảng nhưng không phải là các hàng khác (vì vậy 0 hàng nếu các bảng có dữ liệu giống hệt nhau):

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

Hoặc EXISTSđể trả về chỉ một giá trị boolean hoặc một chuỗi với một trong 2 kết quả có thể:

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

Đã thử nghiệm tại SQLfiddle


Ngoài ra, không EXCEPTloại bỏ trùng lặp (điều đó không đáng lo ngại nếu các bảng của bạn có một số PRIMARY KEYhoặc UNIQUEràng buộc nhưng có thể là nếu bạn đang so sánh kết quả của các truy vấn tùy ý có khả năng tạo ra các hàng trùng lặp).

Một điều khác mà EXCEPTtừ khóa thực hiện là nó coi NULLcác giá trị là giống hệt nhau, vì vậy nếu bảng Acó một hàng (1,2,NULL)và bảng Bcó một hàng (1,2,NULL), truy vấn đầu tiên sẽ không hiển thị các hàng này và truy vấn thứ hai sẽ trả về 'same'nếu hai bảng không có hàng khác.

Nếu bạn muốn tính các hàng như vậy là khác nhau, bạn có thể sử dụng một biến thể cho FULL JOINcâu trả lời của gsiems , để có được tất cả các hàng (khác nhau):

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

và để có được câu trả lời có / không:

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 ;

Nếu tất cả các cột của hai bảng không thể rỗng, hai cách tiếp cận sẽ cho câu trả lời giống hệt nhau.


Có thể có một số phương pháp hiệu quả hơn, không chắc chắn.
ypercubeᵀᴹ

@FaheemMitha bạn có thể sử dụng điều này để so sánh ít cột hơn tất cả. Chỉ cần sử dụng SELECT <column_list> FROM athay vìTABLE a
ypercubeᵀᴹ

2
Các EXCEPTtruy vấn là một người đẹp!
Erwin Brandstetter

Truy vấn EXCEPT thật ngọt ngào!
sharadov

1

Bạn cần ngoại trừ mệnh đề Một cái gì đó như

SELECT * FROM first_table
EXCEPT
SELECT * FROM second_table

Điều này trả về tất cả các hàng từ bảng đầu tiên không nằm trong bảng thứ hai


0

Nhìn vào mã được liên kết bạn không hiểu:

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

Nước sốt bí mật được sử dụng uniontrái ngược với union all. Cái trước chỉ giữ lại các hàng riêng biệt trong khi cái sau giữ các bản sao ( tham chiếu ). Nói cách khác, các truy vấn lồng nhau nói "hãy cho tôi tất cả các hàng và cột từ EmpDtl1 và ngoài ra các truy vấn từ EmpDtl2 chưa có trong EmpDtl1". Tổng số truy vấn con này sẽ bằng với số lượng EmpDtl1 khi và chỉ khi EmpDtl2 không đóng góp bất kỳ hàng nào cho kết quả, tức là hai bảng giống hệt nhau.

Ngoài ra, kết xuất các bảng theo thứ tự chính vào hai tệp văn bản và sử dụng công cụ so sánh bạn chọn.


3
Điều này sẽ không phát hiện trường hợp khi EmpDtl2ít hàng hơn EmpDtl1và tất cả các hàng hiện có tồn tại EmpDtl1.
a_horse_with_no_name
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.