ตรวจสอบว่าสองตารางมีเนื้อหาเหมือนกันใน 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:

หากมีปัญหากับข้อมูล null ที่เป็นไปได้ (เช่นคอลัมน์ 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 ;

นี่จะไม่ล้มเหลวหรือไม่ถ้าวาลเป็นโมฆะ?
Amit Goldstein

@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ᵀᴹ

@FaheemMitha คุณสามารถใช้สิ่งนี้เพื่อเปรียบเทียบคอลัมน์น้อยกว่าทั้งหมด เพียงใช้SELECT <column_list> FROM aแทนTABLE a
ypercubeᵀᴹ

2
การEXCEPTค้นหาคือความสวยงาม!
Erwin Brandstetter

ข้อความค้นหายกเว้นว่าหวาน!
sharadov

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.