左连接在其他表上具有条件的表上

时间:2017-03-23 10:01:06

标签: sql oracle left-join

我正在尝试连接多个表,并在加入第二个表时在第三个表上有一个子句。我已经尝试过where子句但它适用于整个结果,当我只想使第二个表中的列无效时。

举一个例子,它会更清楚。 我有4张桌子:

CREATE TABLE A (ID INTEGER PRIMARY KEY);
CREATE TABLE B (ID INTEGER PRIMARY KEY, A_ID INTEGER, C_ID INTEGER, D_ID INTEGER);
CREATE TABLE C (ID INTEGER PRIMARY KEY, CONDITIONS INTEGER);
CREATE TABLE D (ID INTEGER PRIMARY KEY, CONDITIONS INTEGER);

表B将表A连接到表C和D。

示例数据将是:

INSERT INTO A VALUES (1);
INSERT INTO A VALUES (2);
INSERT INTO A VALUES (3);

INSERT INTO C VALUES (1, 1);
INSERT INTO C VALUES (2, 1);
INSERT INTO C VALUES (3, 0);

INSERT INTO D VALUES (1, 0);
INSERT INTO D VALUES (2, 0);

INSERT INTO B VALUES (1, 1, 1, NULL);
INSERT INTO B VALUES (2, 1, 2, NULL);
INSERT INTO B VALUES (3, 1, 3, NULL);
INSERT INTO B VALUES (4, 2, NULL, 1);
INSERT INTO B VALUES (5, 2, NULL, 2);

直接左连接:

SELECT A.ID, B.ID, C.ID, D.ID 
FROM A 
LEFT JOIN B ON B.A_ID = A.ID 
LEFT JOIN C ON B.C_ID = C.ID 
LEFT JOIN D ON B.D_ID = D.ID;

返回数据:

╔══════╦══════╦══════╦══════╗
║ A.id ║ B.id ║ C.id ║ D.id ║
╠══════╬══════╬══════╬══════╣
║   1  ║   1  ║   1  ║ null ║
║   1  ║   2  ║   2  ║ null ║
║   1  ║   3  ║   3  ║ null ║
║   2  ║   4  ║ null ║   1  ║
║   2  ║   5  ║ null ║   2  ║
║   3  ║ null ║ null ║ null ║
╚══════╩══════╩══════╩══════╝

我要做的是使用C和D表中的数据过滤B表。 如果我只是在请求中添加where条件:

SELECT A.ID, B.ID, C.ID, D.ID 
FROM A 
LEFT JOIN B ON B.A_ID = A.ID 
LEFT JOIN C ON B.C_ID = C.ID 
LEFT JOIN D ON B.D_ID = D.ID 
WHERE (C.ID IS NULL OR C.CONDITIONS = 1)
AND (D.ID IS NULL OR D.CONDITIONS = 1);

它返回:

╔══════╦══════╦══════╦══════╗
║ A.id ║ B.id ║ C.id ║ D.id ║
╠══════╬══════╬══════╬══════╣
║   1  ║   1  ║   1  ║ null ║
║   1  ║   2  ║   2  ║ null ║
║   3  ║ null ║ null ║ null ║
╚══════╩══════╩══════╩══════╝

这是逻辑但不是我想要的。我想要的是:

╔══════╦══════╦══════╦══════╗
║ A.id ║ B.id ║ C.id ║ D.id ║
╠══════╬══════╬══════╬══════╣
║   1  ║   1  ║   1  ║ null ║
║   1  ║   2  ║   2  ║ null ║
║   2  ║ null ║ null ║ null ║
║   3  ║ null ║ null ║ null ║
╚══════╩══════╩══════╩══════╝

使用A.ID = 2保留一行但在B中找不到具有C和D匹配条件的值。

我试图将条件放在加入C和D表的ON子句中,但它保留了来自B的数据:

╔══════╦══════╦══════╦══════╗
║ A.id ║ B.id ║ C.id ║ D.id ║
╠══════╬══════╬══════╬══════╣
║   1  ║   1  ║   1  ║ null ║
║   1  ║   2  ║   2  ║ null ║
║   1  ║   3  ║ null ║ null ║
║   2  ║   4  ║ null ║ null ║
║   2  ║   5  ║ null ║ null ║
║   3  ║ null ║ null ║ null ║
╚══════╩══════╩══════╩══════╝

我现在没有想法来做这个伎俩。

3 个答案:

答案 0 :(得分:4)

您需要做的是首先从b表到cd表的左外连接,然后再返回到a的外连接表格cd条件列中是否存在值。像这样:

SELECT a.id a_id, b2.b_id, b2.c_id, b2.d_id
FROM   a
       LEFT OUTER JOIN (SELECT b.id b_id,
                               b.a_id,
                               c.id c_id,
                               d.id d_id
                        FROM   b
                               LEFT OUTER JOIN c ON b.c_id = c.id AND c.conditions = 1
                               LEFT OUTER JOIN d ON b.d_id = d.id AND d.conditions = 1) b2
         ON a.id = b2.a_id AND COALESCE(b2.c_id, b2.d_id) IS NOT NULL
ORDER BY a.id, b2.b_id, b2.c_id, b2.d_id;

      A_ID       B_ID       C_ID       D_ID
---------- ---------- ---------- ----------
         1          1          1 
         1          2          2 
         2                       
         3                       

(感谢Alex Poole发现我编辑的输出问题!)

ETA:

这也可以写成:

SELECT a.id a_id, b.id b_id, c.id c_id, d.id d_id
FROM   a
       LEFT OUTER JOIN (b
                        LEFT OUTER JOIN c ON b.c_id = c.id AND c.conditions = 1
                        LEFT OUTER JOIN d ON b.d_id = d.id AND d.conditions = 1)
         ON a.id = b.a_id AND COALESCE(c.id, d.id) IS NOT NULL
ORDER BY a.id, b.id, b.c_id, b.d_id;

这更简单,但可能更难破译意图(因此将来难以维护)。我已将其添加到此处,因为我不知道这是有效的语法,您可能觉得它对您更有效。

答案 1 :(得分:0)

我正在添加另一个答案,因为我确实删除了前一个答案,因为它不正确。我认为这是正确的逻辑:

SELECT A.ID, B.ID, C.ID, D.ID 
FROM A LEFT JOIN
     (B LEFT JOIN
      C
      ON B.C_ID = C.ID AND C.CONDITIONS = 1 LEFT JOIN
      D
      ON B.D_ID = D.ID AND D.CONDITIONS = 1
     )
     ON B.A_ID = A.ID AND
        (C.ID IS NOT NULL OR D.ID IS NOT NULL);

测试时会返回正确的结果。

这是一个有趣的问题。我们的想法是使用括号来“延迟”AB之间的比较。这允许条件还确定CD上是否匹配。

答案 2 :(得分:0)

实际上我在ON子句中找到了另一种使用子查询的方法:

SELECT A.ID, B.ID, C.ID, D.ID 
FROM A 
LEFT JOIN B ON B.A_ID = A.ID 
    AND (B.C_ID IS NULL OR B.ID IN (SELECT B.ID FROM B JOIN C ON C.ID = B.C_ID AND C.CONDITIONS = 1)
    AND (B.D_ID IS NULL OR B.ID IN (SELECT B.ID FROM B JOIN D ON D.ID = B.D_ID AND D.CONDITIONS = 1)
LEFT JOIN C ON B.C_ID = C.ID 
LEFT JOIN D ON B.D_ID = D.ID;

我不知道在A表和B,C和D大表上有另一个条款会更好地解决问题。