检查两个表在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,但是我不了解所使用的逻辑。

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


3
您可以使用EXCEPT,检查了这个问题:一个有效的方式SQL来比较两个大型数据集
ypercubeᵀᴹ

pg_comparator进行有效的表内容比较和同步
natmaka

@natmaka这应该是一个单独的答案吗?
Faheem Mitha

Answers:


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 ;

如果val可为空,这不会失败吗?
阿米特·戈德斯坦

@AmitGoldstein-空值将是一个问题。请参阅我的附录,以找到一种可能的解决方案。
gsiems

30

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

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

或with 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 KEYUNIQUE约束,则不必担心,但如果您正在比较可能产生重复行的任意查询的结果,则可以担心)。

EXCEPT关键字所做的另一件事是,它将NULL值视为相同,因此,如果table A的行与(1,2,NULL)并且table 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查询很漂亮!
欧文·布兰德斯特

EXCEPT查询很贴心!
sharadov

1

您需要Except子句之类的

SELECT * FROM first_table
EXCEPT
SELECT * FROM second_table

这将从第一张表返回所有不在第二张表中的行


0

查看您不了解的链接代码:

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

秘密调味料正在使用union而不是union all。前者仅保留不同的行,而后者则保留重复的行(参考)。换句话说,嵌套查询说“给我EmpDtl1中的所有行和列,以及EmpDtl1中尚未存在的所有行和列”。当且仅当EmpDtl2不对结果贡献任何行(即两个表相同)时,此子查询的计数才等于EmpDtl1的计数。

或者,按键顺序将表转储到两个文本文件中,并使用您选择的比较工具。


3
时,则不会检测的情况下EmpDtl2具有更低比行EmpDtl1和所有现有行做存在EmpDtl1
a_horse_with_no_name 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.