Odd FULL OUTER JOIN "ON" 子句?

Odd FULL OUTER JOIN "ON" Clause?

我运行遇到了一个有点令人困惑的问题。

简化查询:(假设 ID 是每个 table 中的主键)

SELECT
A.ID,
A.Data,
B.Data,
C.Data
FROM
A FULL OUTER JOIN
B ON A.ID = B.ID FULL OUTER JOIN
C ON A.ID = C.ID

我正在使用 FULL OUTER JOIN,因为在我的情况下,不能保证任何 ID 都在所有三个 table 中,但如果它在多个 table 中table,我想在一行中查看它的所有数据。

这是我很快意识到的问题:如果 tables BC 中都有一个 ID(但 A), 那么你 运行 进入以下问题:

  1. 对于未出现在 table A 中的 IDID 字段是 NULL。这是有道理的,因为查询选择 A.ID。我找到了一个非常简单的方法来解决这个问题,那就是使用 COALESCE(即 COALESCE(A.ID,B.ID,C.ID))。

  2. 不在 table A 中的 ID 的数据在两个单独的行中返回。 (一行有NULL的是B的数据,另一行有NULL的是C的数据。)想了想,这也使得因为上面查询的编写方式。 tables BC 都基于 table A 加入,所以如果 ID 不在 table 中A,则查询与ID in tables B and C join 没有关系。我也找到了解决这个问题的方法,就是在 ON 子句中明确指定与 every table 的关系,用 [=42= 分隔]的。

因此进行以下更改将解决这两个问题:

SELECT
COALESCE(A.ID,B.ID,C.ID),
A.Data,
B.Data,
C.Data
FROM
A FULL OUTER JOIN
B ON A.ID = B.ID FULL OUTER JOIN
C ON A.ID = C.ID OR B.ID = C.ID

这很好用,但我花了一些时间才弄明白,未来 运行 跨这个查询的人员可能会很奇怪,因为使用 COALESCE 和一堆 ORON子句中乍一看似乎是多余的,但实际上两者都是需要的。

对于较大的查询,这也会变得非常混乱,因为 ON 子句的大小对于每个以这种方式连接的 table 都是复合的。

我的问题是:是否有一些其他内置方法或其他技巧来处理 OUTER JOIN 这种类型的已经考虑到这些您不需要考虑的额外条件INNER JOIN的?

想象一个交叉表查询,或者一个执行此操作的数据透视表:

这是 excel 中的示例。

这种方式不是连接,而是在您的 ID 列上旋转,并且数据是否存在于其他 table 中是无关紧要的;只要 1 table 有数据,table 中的值就会显示。

这是我使用 Excel:

的视觉效果

SO ID 是您在每个 table 中的 ID。 Table 是涉及的 3 table。数据是与每个 ID 关联的值。通过旋转数据,您可以看到 table A 在所有 3 条记录中都有值,而 b 仅在记录 2 和 3 上有值,而 c 仅在记录 1 和 2 上有值。在 SQL 中使用动态数据透视服务器,我相信你可以完成同样的事情。然而,这需要使用动态 SQL:示例:SQL Server dynamic PIVOT query?

我认为这会满足您的要求:

FROM A FULL OUTER JOIN B 
  ON A.ID = B.ID 
OUTER JOIN C ON A.ID = C.ID or B.ID = C.ID

这会为您提供 A 和 B 中的所有行,以及 C 中与其中任何一个匹配的任何内容。

if it is in more than one table, I want to see all the data

要排除 ID 仅出现在一个 table 中的情况,请添加一堆指定允许组合的 WHERE 子句,

where A.ID is not NULL and B.ID is not NULL 
   or B.ID is not NULL and C.ID is not NULL 
   or A.ID is not NULL and C.ID is not NULL 
     

这是另一种方式。它不一定或多或少复杂或高效。你需要检查你的情况。如前所述,必须这样做可能表明存在建模问题。

SELECT ID, MAX(Data1), MAX(Data2), MAX(Data3)
FROM
(
SELECT A.ID, A.Data1, NULL, NULL
FROM A
UNION ALL
SELECT B.ID, NULL, B.Data2, NULL
FROM B
UNION ALL
SELECT C.ID, NULL, NULL, C.Data3
FROM C
) T
GROUP BY ID

感谢@xQbert 提供的数据。

您可以使用 CTE(具有唯一 ID 列表)实现相同的效果,然后进行交叉应用。

DECLARE @a table(id int, data int)
DECLARE @b table(id int, data int)
DECLARE @c table(id int, data int)

insert into @a values(1, 9), (2, 8),(3,7)
insert into @b values(2, 6), (3, 5)
insert into @c values(1, 4), (2, 5)

;WITH CTE_IDs as
(
SELECT ID from @a
UNION
SELECT ID FROM @b
UNION
SELECT ID FROM @c
)
SELECT c.ID,t.* FROM CTE_IDs as c
cross apply
(
VALUES((select data from @a where id = c.id),
(select data from @b where id = c.id),
(select data from @c where id = c.id)) 
) as t(a_data,b_data,c_data)
ID a_data b_data c_data
1 9 NULL 4
2 8 6 5
3 7 5 NULL