admin

左外部联接是关联的吗?

sql

很容易理解为什么左外部联接不是可交换的,但是我很难理解它们是否是可交换的。几家在线资源表明不是,但我还没有说服自己就是这种情况。

假设我们有三个表:A,B和C。

令A包含ID和B_ID两列,其中ID是表A的主键,B_ID是与表B的主键相对应的外键。

令B包含两列ID和C_ID,其中ID是表B的主键,C_ID是与表C的主键相对应的外键。

让C包含ID和VALUE两列,其中ID是表C的主键,而VALUE仅包含一些任意值。

那不(A left outer join B) left outer join C应该等于A left outer join (B left outer join C)吗?


阅读 198

收藏
2021-05-10

共1个答案

admin

正如您的问题似乎暗示的那样,如果您假设要联接外键,那么可以,我认为OUTER JOIN可以保证是关联的,如Przemyslaw
Kruglej的答案所涵盖:

另一种查看方式:

既然您说过表A与B联接,而B与C联接,那么:

第一次加入A和B时,剩下的都是A的所有记录。其中一些具有B的值。现在,对于那些从B获得值的行中的某些记录,您可以从C获得值。
当您第一次连接B和C时,您将遍历整个表B,其中的某些记录都具有C的值。现在,您取走了A的所有记录,并将其中的某些记录与B的所有行都连接了C。 ,同样,您从A获得所有行,但是其中一些具有来自B的值,其中一些具有来自C的值。
在您所描述的条件下,根据LEFT连接的顺序,我看不到任何数据丢失的可能性。

根据蒂拉克在回答中提供的数据(现已删除),我构建了一个简单的测试用例:

CREATE TABLE atab (id NUMBER, val VARCHAR2(10));
CREATE TABLE btab (id NUMBER, val VARCHAR2(10));
CREATE TABLE ctab (id NUMBER, val VARCHAR2(10));

INSERT INTO atab VALUES (1, 'A1');
INSERT INTO atab VALUES (2, 'A2');
INSERT INTO atab VALUES (3, 'A3');

INSERT INTO btab VALUES (1, 'B1');
INSERT INTO btab VALUES (2, 'B2');
INSERT INTO btab VALUES (4, 'B4');

INSERT INTO ctab VALUES (1, 'C1');
INSERT INTO ctab VALUES (3, 'C3');
INSERT INTO ctab VALUES (5, 'C5');

SELECT ab.aid, ab.aval, ab.bval, c.val AS cval
  FROM (
    SELECT a.id AS aid, a.val AS aval, b.id AS bid, b.val AS bval
      FROM atab a LEFT OUTER JOIN btab b ON (a.id = b.id)
    ) ab
    LEFT OUTER JOIN ctab c ON (ab.bid = c.id)
ORDER BY ab.aid
;
       AID AVAL       BVAL       CVAL     
---------- ---------- ---------- ----------
         1 A1         B1         C1         
         2 A2         B2                    
         3 A3                                   
SELECT a.id, a.val AS aval, bc.bval, bc.cval
  FROM
    atab a
    LEFT OUTER JOIN (
      SELECT b.id AS bid, b.val AS bval, c.id AS cid, c.val AS cval
        FROM btab b LEFT OUTER JOIN ctab c ON (b.id = c.id)
    ) bc
      ON (a.id = bc.bid)
ORDER BY a.id
;
        ID AVAL       BVAL       CVAL     
---------- ---------- ---------- ----------
         1 A1         B1         C1         
         2 A2         B2                    
         3 A3                             

在此特定示例中,似乎两个解决方案都给出了相同的结果。我想不出任何其他数据集,这些数据集会使这些查询返回不同的结果。

但是,鉴于您实际上尚未指定JOIN条件,因此从理论上讲,正确的答案是不,不能保证它们是关联的。有两种简单的方法可以违背带有反常ON子句的关联性。

1. JOIN条件之一涉及所有3个表中的列

这是一种违反关联性的廉价方法,但是严格来说,您的问题中没有任何内容可以禁止它。使用问题中建议的列名,考虑以下两个查询:

-- This is legal
SELECT * FROM (A JOIN B ON A.b_id = B.id) 
              JOIN C ON (A.id = B.id) AND (B.id = C.id)


-- This is not legal
SELECT * FROM A
              JOIN (B JOIN C ON (A.id = B.id) AND (B.id = C.id))
              ON A.b_id = B.id

最底下的查询甚至不是有效的查询,但最顶上的查询是。显然,这违反了关联性。

2.尽管一个表中的所有字段均为NULL,但仍可以满足JOIN条件之一

这样,根据JOIN的顺序,我们甚至可以在结果集中拥有不同数量的行。例如,让在B上加入A的条件为A.b_id = B.id,但在C上加入B的条件为B.id IS NULL

因此,我们得到了这两个查询,它们的输出截然不同:

SELECT * FROM (A LEFT OUTER JOIN B ON A.b_id = B.id) 
              LEFT OUTER JOIN C ON B.id IS NULL;


SELECT * FROM A 
              LEFT OUTER JOIN (B LEFT OUTER JOIN C ON B.id IS NULL)
              ON A.b_id = B.id;

\

2021-05-10