查找两个表之间不匹配的行

Find unmatched rows between two tables

鉴于此设置:

CREATE TABLE table1 (column1 text, column2 text);
CREATE TABLE table2 (column1 text, column2 text);

INSERT INTO table1 VALUES
   ('A', 'A')
 , ('B', 'N')
 , ('C', 'C')
 , ('B', 'A');

INSERT INTO table2 VALUES
   ('A', 'A')
 , ('B', 'N')
 , ('C', 'X')
 , ('B', 'Y');

如何在这两个 table 之间找到 (column1, column2) 缺失组合?其他 table.

中的行不匹配

给定示例的预期结果为:

  C | C
  B | A
  C | X
  B | Y

可能会有重复的条目,因此我们希望忽略这些条目。

您可以尝试将 not exists 与子查询一起使用,然后使用 UNION ALL

select Column1,Column2   from table1 t1 
where NOT exists 
(
    select 1 
    FROM table2 t2
    where t1.Column1 = t2.Column1 or t1.Column2 = t2.Column2
)
UNION ALL
select Column1,Column2  from table2 t1 
where NOT exists 
(
    select 1 
    FROM table1 t2
    where t1.Column1 = t2.Column1 or t1.Column2 = t2.Column2
)

一种方法是union all:

select t1.col1, t1.col2
from t1
where (t1.col1, t1.col2) not in (select t2.col1, t2.col2 from t2)
union all
select t2.col1, t2.col2
from t2
where (t2.col1, t2.col2) not in (select t1.col1, t1.col2 from t1);

如果一个table中有重复项,您可以使用select distinct删除它们。 和 table 之间没有重复的危险。

您可以尝试设置操作。 EXCEPT 在 table 中查找行,但在另一个中找不到,UNION 将部分结果合并。

(SELECT column1,
        column2
        FROM table1
 EXCEPT
 SELECT column1,
        column2
        FROM table2)
UNION
(SELECT column1,
        column2
        FROM table2
 EXCEPT
 SELECT column1,
        column2
        FROM table1);

如果您不需要重复消除,您可以尝试使用 ALL 变体(EXCEPT ALLUNION ALL)。它们通常更快,因为 DBMS 不必查找和消除重复项。

似乎是集合操作的完美任务:

  ( --all rows from table 1 missing in table 2
    select *
    from table1
    except 
    select *
    from table2
  )
  union all -- both select return distinct rows
  ( -- all rows in table 2 missing in table 1
    select *
    from table2
    except 
    select *
    from table1
  )

这个看似简单的任务,魔鬼藏在细节中

短且最快:

SELECT col1, col2
FROM        (SELECT col1, col2, TRUE AS x1 FROM t1) t1
FULL   JOIN (SELECT col1, col2, TRUE AS x2 FROM t2) t2 USING (col1, col2)
WHERE  (x1 AND x2) IS NULL;

FULL [OUTER] JOIN包括两侧的所有行,但为缺失行的列填充 NULL 值。 WHERE 条件 (x1 AND x2) IS NULL 标识这些不匹配的行。等价于:WHERE x1 IS NULL OR x2 IS NULL.

要消除 重复的 对,请在末尾添加 DISTINCT(或 GROUP BY)- 更便宜几个 骗子:

SELECT DISTINCT col1, col2
FROM ...

如果你的两边都有 许多 个骗子,在 之前折叠 更便宜加入:

SELECT col1, col2
FROM        (SELECT DISTINCT col1, col2, TRUE AS x1 FROM t1) t1
FULL   JOIN (SELECT DISTINCT col1, col2, TRUE AS x2 FROM t2) t2 USING (col1, col2)
WHERE  (x1 AND x2) IS NULL;

如果可以有 NULL 值,那就更复杂了。 DISTINCT / DISTINCT ONGROUP BY 将它们视为相等(因此具有 NULL 值的欺骗在上面的子查询中被折叠)。但是 JOINWHERE 条件必须评估为 TRUE 行才能通过。 NULL 值 not 被认为是相等的,FULL [OUTER] JOIN 永远不会找到包含 NULL 的对的匹配项。这可能是可取的,也可能不是可取的。您只需要了解差异并定义您的要求即可。

考虑在 SQL Fiddle

中添加的演示

如果没有NULL值,没有重复,但是在每个table中定义了一个额外的列NOT NULL,就像主键一样,让我们​​命名每个id,然后它可以像:

一样简单
SELECT col1, col2
FROM   t1
FULL   JOIN t2 USING (col1, col2)
WHERE  t1.id IS NULL OR t2.id IS NULL;

相关: