【问题标题】:Alternatives to a conditional full outer join有条件的完全外连接的替代方案
【发布时间】:2020-02-28 21:55:51
【问题描述】:

我需要比较两个表中的记录:X 和 Y。每条记录都有两个 id:ID1 和 ID2。 ID1 或 ID2 在任一表中都可以为空,但不能同时为空。我需要生成一个包含两个表中所有信息的视图:

  • X.ID1 = Y.ID1 和 X.ID2 = Y.ID2 的行
  • X.ID1 = Y.ID1 但 X.ID2 Y.ID2 的行
  • X.ID1 Y.ID1 但 X.ID2 = Y.ID2 的行
  • X.ID1 和 Y.ID1 根本没有任何匹配项的行
  • X.ID2 和 Y.ID2 根本没有任何匹配项的行

例子:

X:                   Y:
|---------------|    |---------------|
|  ID1  |  ID2  |    |  ID1  |  ID2  |
|---------------|    |---------------|
|   1   |   A   |    |   1   |   A   |
|   2   |   B   |    |   2   |   C   |
|   3   | NULL  |    | NULL  |   B   |
| NULL  |   D   |    |   5   | NULL  |
|---------------|    |---------------|

Output:
|---------------------------------------|
|  XID1 |  YID1 |  XID2 |  YID2 |  SRC  |
|---------------------------------------|
|   1   |   1   |   A   |   A   |  X+Y  |
|   2   |   2   |   B   |   C   |  X+Y  |
|   3   | NULL  | NULL  | NULL  |   X   |
| NULL  |   5   | NULL  | NULL  |   Y   |
|   2   | NULL  |   B   |   B   |  X+Y  |
| NULL  |   2   | NULL  |   C   |   Y   |
| NULL  | NULL  |   D   | NULL  |   X   |
|---------------------------------------|

我的第一个明显的解决方案是做一个FULL OUTER JOIN:

SELECT … FROM X FULL OUTER JOIN Y ON X.ID1 = Y.ID1 OR X.ID2 = Y.ID2

这可行,但连接中的条件性能很差,并且此视图最多需要一分钟才能运行。删除条件将执行时间缩短到不到一秒,但随后我失去了其中一个 ID 的匹配。

如何在不使用条件连接的情况下优雅地实现上述目标?我试过了:

  • 通过连接两个 ID 进行连接,但这仅在两个 ID 匹配时才匹配
  • 执行CROSS JOIN 并按X.ID1=Y.ID1 OR X.ID2=Y.ID2 过滤,但这会丢失没有任何匹配项的情况。这是最有前途的方法。
  • 对 X 和 Y 执行 UNION ALL,然后按 ID1 和 ID2 分组,但这再次仅在两个 ID 匹配时才匹配

【问题讨论】:

  • (1) MySQL 不支持full join,所以我去掉了标签。 (2) 提供样本数据和期望的结果。

标签: sql sql-server


【解决方案1】:

您可以尝试将其分解为多个连接。我认为逻辑是:

SELECT …
FROM X JOIN
     Y
     ON X.ID1 = Y.ID1 
UNION ALL
SELECT …
FROM X JOIN
     Y
     ON X.ID1 <> Y.ID1 AND X.ID2 = Y.ID2
UNION ALL
SELECT ...
FROM X 
WHERE NOT EXISTS (SELECT 1 FROM Y WHERE Y.ID1 = X.ID1) AND
      NOT EXISTS (SELECT 1 FROM Y WHERE Y.ID2 = X.ID2) 
UNION ALL
SELECT ...
FROM Y 
WHERE NOT EXISTS (SELECT 1 FROM X WHERE Y.ID1 = X.ID1) AND
      NOT EXISTS (SELECT 1 FROM X WHERE Y.ID2 = X.ID2) ;

【讨论】:

  • 谢谢戈登!这需要对 NULL ID 进行更多检查,但我明白了。你认为没有办法做到这一点涉及更少的复制/粘贴?
【解决方案2】:

如果我正确阅读了您的条件,您可以尝试这样的事情。将两个左连接在一起并取两个集合中的一个不同的集合。

SELECT DISTINCT ... FROM (
  SELECT … FROM X LEFT JOIN Y ON X.ID1 = Y.ID1
  UNION ALL
  SELECT … FROM X LEFT JOIN Y ON X.ID2 = Y.ID2
  UNION ALL
  SELECT … FROM Y LEFT JOIN X ON Y.ID1 = X.ID1 WHERE X.ID1 is null
  UNION ALL
  SELECT … FROM Y LEFT JOIN X ON Y.ID2 = X.ID2 WHERE X.ID2 is null
)

在我必须在连接中执行 OR 或两个左连接的并集之间做出选择的情况下,我发现并集更快。

编辑:更新为在左侧也包含 Y。

【讨论】:

  • 很有希望,但这不会返回 Y 中不在 X 中的行。
  • 更新为包括Y。
猜你喜欢
  • 1970-01-01
  • 2016-09-06
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 2020-03-17
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
相关资源
最近更新 更多