在PostgreSQL中检查两个表的内容是否相同

Fah*_*tha 35 postgresql duplication

这已经在 Stack Overflow 上问过了,但仅限于 MySQL。我正在使用 PostgreSQL。不幸的是(而且令人惊讶的是)PostgreSQL 似乎没有像CHECKSUM table.

PostgreSQL 解决方案会很好,但通用的解决方案会更好。我找到了http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-data,但我不明白使用的逻辑。

背景:我重新编写了一些数据库生成代码,所以我需要检查新旧代码是否产生相同的结果。

ype*_*eᵀᴹ 39

您可以使用EXCEPT运算符。例如,如果表具有相同的结构,以下将返回一个表中的所有行而不是另一个表中的所有行(如果表具有相同的数据,则为 0 行):

(TABLE a EXCEPT TABLE b)
UNION ALL
(TABLE b EXCEPT TABLE a) ;
Run Code Online (Sandbox Code Playgroud)

或者 withEXISTS只返回一个布尔值或一个带有 2 个可能结果之一的字符串:

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

SQLfiddle测试


也不是EXCEPT删除重复项(如果您的表有一些PRIMARY KEYUNIQUE约束,则不必担心,但如果您正在比较可能产生重复行的任意查询的结果,则可能需要担心)。

EXCEPT关键字所做的另一件事是它将NULL值视为相同的,因此如果 tableA有一行 with(1,2,NULL)并且 tableB有一行 with (1,2,NULL),则第一个查询将不会显示这些行,'same'如果两个表没有其他行,则第二个查询将返回。

如果您想将此类行计算为不同,您可以使用 gsiemsFULL JOIN答案的变体来获取所有(不同)行:

SELECT *
FROM a NATURAL FULL JOIN b
WHERE a.some_not_null_column IS NULL 
   OR b.some_not_null_column IS NULL ;
Run Code Online (Sandbox Code Playgroud)

并获得是/否的答案:

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 ;
Run Code Online (Sandbox Code Playgroud)

如果两个表的所有列都不可为空,则两种方法将给出相同的答案。

  • `EXCEPT` 查询很漂亮! (3认同)

gsi*_*ems 29

一种选择是以以下形式在两个表之间使用 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 ;
Run Code Online (Sandbox Code Playgroud)

例如:

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 ;
Run Code Online (Sandbox Code Playgroud)

将返回 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 ;
Run Code Online (Sandbox Code Playgroud)

返回希望计数为 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 ;
Run Code Online (Sandbox Code Playgroud)