外部联接中出现意外的NULL

时间:2019-04-30 16:22:02

标签: sql sql-server sql-server-2008-r2

我有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。除了Key1和Key2不是TableC主键的一部分之外,Key3无关紧要。

我正在尝试编写结合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

3 个答案:

答案 0 :(得分:1)

  

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

完整的外部联接与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

这将返回到下一个阶段的以下虚拟表(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 |
+----+----+------+----+

然后将其完全外部联接到B上。 B的内容是

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

这两个具有谓词{{​​1}}的结果集中的INNER JOIN仅返回2行。

ON (TableB.Key1 = [A1] AND [C1] IS NULL) OR ([C1] = TableB.Key1 AND [C2] = TableB.Key2)

根据+----+----+----+----+----+----+ | A1 | B1 | B2 | C1 | C2 | C3 | +----+----+----+----+----+----+ | 1 | 1 | 1 | 1 | 1 | 3 | | 1 | 1 | 3 | 1 | 3 | 4 | +----+----+----+----+----+----+ 重新添加VT1中不匹配的行(其中LEFT JOINC31的行)

2

+----+------+------+----+------+----+ | 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 JOINB22的行)

给您最终结果

4

答案 1 :(得分:0)

这就是您想要的-注意...您想要在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

答案 2 :(得分:0)

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面,并使用联合将它们拉在一起。

希望这对您有帮助...