如何在 PostgreSQL 中的两个表之间进行 return 差异的查询

How to make query that will return differences between two tables in PostgreSQL

我需要 return 两个 table 之间的差异。

创建临时表

CREATE TEMP TABLE first(
  zoom smallint NOT NULL,
  x integer NOT NULL,
  y integer NOT NULL
);

CREATE TEMP TABLE second(
  zoom smallint NOT NULL,
  x integer NOT NULL,
  y integer NOT NULL
);

插入数据

INSERT INTO first(zoom,x,y) VALUES(5,2,25);
INSERT INTO first(zoom,x,y) VALUES(5,4,45);
INSERT INTO first(zoom,x,y) VALUES(5,7,34);
INSERT INTO first(zoom,x,y) VALUES(5,45,40);
INSERT INTO first(zoom,x,y) VALUES(5,72,63);
INSERT INTO second(zoom,x,y) VALUES(5,2,25);
INSERT INTO second(zoom,x,y) VALUES(5,4,45);
INSERT INTO second(zoom,x,y) VALUES(5,7,34);

想要的结果:

In table first there are extra rows:
5,45,40
5,72,63

编辑

很抱歉,但我现在发现我的原始数据比我提供的样本复杂得多。所以在原始数据中,table 第一个包含 900 行,table 第二个包含 935 行。我假设每个 table 中的行都是不同的,但是我现在不确定,所以我想在查询中包含这个条件。我假设查询将 return 35 行作为差异,因为我非常确信除了这 35 行之外所有 zoom/x/y 都是相同的。但是,现在可能是这样。所以基本上我需要知道的是两个 table 之间的区别是什么,无论哪种方法都是解决它的最佳方法。

我能得到这样的东西吗:

 zoom | x  | y  | first |second
------+----+--- +-------+------
   5  | 45 | 40 |  yes  |  no |

顺序是第一个是,第二个不是

 zoom | x  | y  | first |second
------+----+--- +-------+------
   5  | 45 | 40 |  yes  |  no |
   5  | 45 | 40 |  yes  |  no |
   5  | 45 | 40 |  yes  |  no |

然后第一个没有,第二个是

 zoom | x  | y  | first |second
------+----+--- +-------+------
   5  | 45 | 40 |  no   |  yes |
   5  | 45 | 40 |  no   |  yes |
   5  | 45 | 40 |  no   |  yes |

这是一种方法:

select max(which) as AppearsIn, x, y, zoom
from ((select 'first' as which, x, y, zoom from first) union all
      (select 'second', x, y, zoom from second)
     ) x
group by x, y, zoom
having count(*) = 1;

这假设行在每个 table 中是不同的。

您可以使用EXCEPT

select zoom,x,y from first 
except 
select zoom,x,y from second

或者我遗漏了什么

如果您想要两个表中的不匹配记录,则

select * from
(
select zoom,x,y from first 
except 
select zoom,x,y from second
) a
union all
select * from 
(
select zoom,x,y from second
except 
select zoom,x,y from first 
) b

因为您想比较两个 table 的所有列,您可以在所有列上使用完全外部联接并检查其中一个是否是:

select case 
          when f.zoom is null then 'missing in first'
          when s.zoom is null then 'missing in second'
       end as status, 
       zoom, x, y
from "first" f
  full outer join second s using (zoom, x, y)
where f.zoom is null or s.zoom is null;

基于 using() 的连接将 return 那些不为空的列(并且只有那些列 - 从结果中删除重复的列)

当使用问题中的示例数据时,结果将是:

status            | zoom | x  | y 
------------------+------+----+---
missing in second |    5 | 45 | 40
missing in second |    5 | 72 | 63

如果在第二个 table 中添加了在第一个中不存在的行,例如:

 INSERT INTO second(zoom,x,y) VALUES(15,7,34);

那么结果将是:

status            | zoom | x  | y 
------------------+------+----+---
missing in second |    5 | 45 | 40
missing in second |    5 | 72 | 63
missing in first  |   15 |  7 | 34

如果你也想要可能的重复不匹配,你应该计算它们:

SELECT COALESCE(f.zoom,s.zoom) AS zoom
        , COALESCE(f.x,s.x) AS x
        , COALESCE(f.y,s.y) AS y
        , COALESCE(f.fcnt,0) AS fcnt
        , COALESCE(s.scnt,0) AS scnt
FROM ( SELECT DISTINCT zoom,x,y
        , COUNT (*) AS fcnt
        FROM first
        GROUP BY zoom,x,y
        ) f
FULL OUTER JOIN ( SELECT DISTINCT zoom,x,y
        , COUNT (*) AS scnt
        FROM second
        GROUP BY zoom,x,y
        ) s USING (zoom,x,y)
WHERE f.fcnt IS NULL OR s.scnt IS NULL
        ;