外连接中出现意外的 NULL

Unexpected NULLs in outer join

我有 3 个表要加入。我们称它们为 TableA、TableB 和 TableC:

DECLARE @TableA TABLE
(
    Key1 int,
    PRIMARY KEY
    (
        Key1
    )
)
DECLARE @TableB TABLE
(
    Key1 int,
    Key2 int,
    PRIMARY KEY
    (
        Key1,
        Key2
    )
)
DECLARE @TableC TABLE
(
    Key3 int NOT NULL,
    Key1 int NOT NULL,
    Key2 int NULL,
    PRIMARY KEY
    (
        Key3
    )
)

下面是一些示例数据:

INSERT INTO @TableA (Key1) VALUES (1);
INSERT INTO @TableB (Key1, Key2) VALUES (1, 1), (1, 2), (1, 3), (1, 4)
INSERT INTO @TableC (Key3, Key1, Key2) VALUES (1, 1, NULL), (2, 1, NULL), (3, 1, 1), (4, 1, 3)

TableB 和 TableC 都有通过 Key1 到 TableA 的外键。实际上,如果 Key2 不为 null,则 TableC 也可以通过 Key1 和 Key2 组合来引用 TableB,但没有实际的外键。 Key3 无关紧要,除了 Key1 和 Key2 不是 TableC 的主键的一部分。

我正在尝试编写结合了 TableB 和 TableC 的查询:

SELECT
    TableA.Key1 AS [A1],
    TableB.Key1 AS [B1],
    TableB.Key2 AS [B2],
    TableC.Key1 AS [C1],
    TableC.Key2 AS [C2],
    TableC.Key3 AS [C3]
FROM @TableA AS TableA
FULL OUTER JOIN @TableC AS TableC
    ON TableC.Key1 = TableA.Key1
FULL OUTER JOIN @TableB AS TableB
    ON (TableB.Key1 = TableA.Key1 AND TableC.Key1 IS NULL)
    OR (TableC.Key1 = TableB.Key1 AND TableC.Key2 = TableB.Key2)
WHERE (TableA.Key1 = TableB.Key1 OR TableA.Key1 = TableC.Key1)
ORDER BY TableB.Key2, TableC.Key2

我的期望是 TableB 和 TableC 都应该包含它们的所有行,匹配在两个键上匹配的行,以及不匹配的 NULLS。

我希望得到这个:

A1       B1       B2       C1       C2      C3
1        NULL     NULL     1        NULL    1
1        NULL     NULL     1        NULL    2
1        1        1        1        1       3
1        1        2        NULL     NULL    NULL -- THIS ROW IS MISSING
1        1        3        1        3       4
1        1        4        NULL     NULL    NULL -- THIS ROW IS MISSING

但是我得到的是:

A1       B1       B2       C1       C2      C3
1        NULL     NULL     1        NULL    1
1        NULL     NULL     1        NULL    2
1        1        1        1        1       3
1        1        3        1        3       4

如果我注释掉 WHERE 子句,我会得到我期望的所有行,除了 A1 对于缺失的行为 NULL:

A1       B1       B2       C1       C2      C3
1        NULL     NULL     1        NULL    1
1        NULL     NULL     1        NULL    2
1        1        1        1        1       3
NULL     1        2        NULL     NULL    NULL   -- A1 should be 1
1        1        3        1        3       4
NULL     1        4        NULL     NULL    NULL   -- A1 should be 1

为什么 TableA.Key1 返回 NULL 并导致它排除缺少 TableB.Key2 的行?

编辑:

这是我了解自己做错了什么之后的最终固定查询:

SELECT
    TableA.Key1 AS A1,
    Subquery.*
FROM @TableA AS TableA
INNER JOIN
(
    SELECT
        TableB.Key1 AS [B1],
        TableB.Key2 AS [B2],
        TableC.Key1 AS [C1],
        TableC.Key2 AS [C2],
        TableC.Key3 AS [C3]
    FROM @TableC AS TableC
    FULL OUTER JOIN @TableB AS TableB
        ON TableB.Key1 = TableC.Key1 AND TableB.Key2 = TableC.Key2
) AS Subquery 
    ON Subquery.B1 = TableA.Key1 OR Subquery.C1 = TableA.Key1
ORDER BY Subquery.B2, Subquery.C2

Why is TableA.Key1 coming back NULL and causing it to exclude rows where TableB.Key2 is missing?

完整外部联接与 INNER JOIN 相同,但任何一侧的任何不匹配的行都将添加回另一侧的列 NULL

您的查询首先对 AC 进行完全外部联接,因此请先查看其结果。

SELECT
    TableA.Key1 AS [A1],
    TableC.Key1 AS [C1],
    TableC.Key2 AS [C2],
    TableC.Key3 AS [C3]
FROM @TableA AS TableA
FULL OUTER JOIN @TableC AS TableC
    ON TableC.Key1 = TableA.Key1

这个returns下面的虚拟table(VT1)进入下一阶段。由于这与 INNER JOIN 的结果相同,我怀疑它是否需要任何解释。 @TableC 中的每一行都成功匹配了 @TableA 中的单行。

+----+----+------+----+
| A1 | C1 |  C2  | C3 |
+----+----+------+----+
|  1 |  1 | NULL |  1 |
|  1 |  1 | NULL |  2 |
|  1 |  1 | 1    |  3 |
|  1 |  1 | 3    |  4 |
+----+----+------+----+

然后将其完全外部连接到 BB的内容是

+------+------+
| Key1 | Key2 |
+------+------+
|    1 |    1 |
|    1 |    2 |
|    1 |    3 |
|    1 |    4 |
+------+------+

INNER JOIN那两个带谓词ON (TableB.Key1 = [A1] AND [C1] IS NULL) OR ([C1] = TableB.Key1 AND [C2] = TableB.Key2)returns的结果集只有2行。

+----+----+----+----+----+----+
| A1 | B1 | B2 | C1 | C2 | C3 |
+----+----+----+----+----+----+
|  1 |  1 |  1 |  1 |  1 |  3 |
|  1 |  1 |  3 |  1 |  3 |  4 |
+----+----+----+----+----+----+

来自 VT1 的不匹配行按照 LEFT JOIN 添加回(这些是 C312 的行)

+----+------+------+----+------+----+
| A1 |  B1  |  B2  | C1 |  C2  | C3 |
+----+------+------+----+------+----+
|  1 | NULL | NULL |  1 | NULL |  1 |
|  1 | NULL | NULL |  1 | NULL |  2 |
|  1 | 1    | 1    |  1 | 1    |  3 |
|  1 | 1    | 3    |  1 | 3    |  4 |
+----+------+------+----+------+----+

和来自 B 的不匹配行,根据 RIGHT JOIN(这些是 B224 的行)

给你最终结果

+------+------+------+------+------+------+
|  A1  |  B1  |  B2  |  C1  |  C2  |  C3  |
+------+------+------+------+------+------+
| 1    | NULL | NULL | 1    | NULL | 1    |
| 1    | NULL | NULL | 1    | NULL | 2    |
| 1    | 1    | 1    | 1    | 1    | 3    |
| 1    | 1    | 3    | 1    | 3    | 4    |
| NULL | 1    | 2    | NULL | NULL | NULL |
| NULL | 1    | 4    | NULL | NULL | NULL |
+------+------+------+------+------+------+

这就是您想要的——注意...您想要 B 和 C 上的完整外部,因此 A 无关紧要——在您的示例的查询中甚至不需要它,但您可以离开或根据需要进行内部连接(我使用左连接)

SELECT
  TableA.Key1 AS [A1],  -- Probably not needed
  TableB.Key1 AS [B1],
  TableB.Key2 AS [B2],
  TableC.Key1 AS [C1],
  TableC.Key2 AS [C2],
  TableC.Key3 AS [C3]
FROM @TableB AS TableB
FULL OUTER JOIN @TableC AS TableC ON TableB.Key1 = TableC.Key1 and TableB.Key2 = TableC.Key2
LEFT JOIN @TableA AS TableA ON TableB.Key1 = TableA.Key1 -- Probably not needed
SELECT 
a.Key1 AS [A1],
b.Key1 AS [B1],
b.Key2 AS [B2],
c.Key1 AS [C1],
c.Key2 AS [C2],
c.Key3 AS [C3]
FROM @TableB b
LEFT JOIN @TableC c
    ON c.Key2 = b.Key2
INNER JOIN @TableA a
    ON b.Key1 = a.Key1

UNION

SELECT 
    a.Key1 AS [A1],
    b.Key1 AS [B1],
    b.Key2 AS [B2],
    c.Key1 AS [C1],
    c.Key2 AS [C2],
    c.Key3 AS [C3]
FROM @TableC c
LEFT JOIN @TableB b
    ON c.Key2 = b.Key2
INNER JOIN @TableA a
    ON c.Key1 = a.Key1

输出:

A1  B1  B2  C1  C2  C3
1   NULL    NULL    1   NULL    1
1   NULL    NULL    1   NULL    2
1   1   1   1   1   3
1   1   2   NULL    NULL    NULL
1   1   3   1   3   4
1   1   4   NULL    NULL    NULL

我先拿到B面,再拿到C面,用union把他们拉在一起

希望这对你有帮助...