左外连接是否相关联?

Tia*_*ong 23 sql join outer-join

很容易理解为什么左外连接不是可交换的,但是我在理解它们是否是关联时遇到了一些麻烦.一些在线消息来源表示他们不是,但我没有设法说服自己这是事实.

假设我们有三个表: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)

Prz*_*lej 23

在这个帖子中,据说它们不是关联的:LEFT OUTER JOIN是关联的吗?

但是,我在网上找到了一些书,其中OUTER JOIN是关联的,当最左侧和最右侧的表没有共同的属性时(这里).

这是一个图形演示文稿(MSPaint ftw):

左外连接顺序影响的图形表示

另一种看待它的方式:

既然你说表A加入B,而B加入C,那么:

  • 当你第一次加入A和B时,你留下来自A的所有记录.其中一些记录来自B.现在,对于你从B获得价值的一些行,你从C得到了值.
  • 当你第一次加入B和C时,你和整个表B,其中一些记录具有来自C的值.现在,你从A获取所有记录并加入其中一些记录,其中所有行都来自B加入C.再次,你从A获得所有行,但是其中一些具有来自B的值,其中一些具有来自C的值.

我没有看到任何可能的情况,在您描述的条件下,根据LEFT连接的顺序会有数据丢失.

基于Tilak在他的答案(现已删除)中提供的数据,我构建了一个简单的测试用例:

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
;
Run Code Online (Sandbox Code Playgroud)
       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
;
Run Code Online (Sandbox Code Playgroud)
        ID AVAL       BVAL       CVAL     
---------- ---------- ---------- ----------
         1 A1         B1         C1         
         2 A2         B2                    
         3 A3                            

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

检查SQLFiddle:

  • @Fabian我明白你的意思了,谢谢你的链接.在我的回答中,我特别关注问题作者提出的案例,我没有尝试将其应用于每个左外连接案例.不确定作者是否仅对该特定案例感兴趣,或者他想要一般答案.无论如何,正如你所说,它可能是或可能不是关联的,这一切都取决于连接的编写方式. (2认同)

Mar*_*ery 15

如果你假设你正在加入一个外键,因为你的问题似乎暗示,那么是的,我认为OUTER JOIN保证是联想的,正如Przemyslaw Kruglej所说的那样.

但是,鉴于您实际上没有指定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
Run Code Online (Sandbox Code Playgroud)

底部查询甚至不是有效查询,但顶部查询是.显然,这违反了相关性.

2.尽管一个表中的所有字段都是NULL,但可以满足其中一个JOIN条件

这样,我们的结果集中甚至可以有不同数量的行,具体取决于JOIN的顺序.例如,让B上的JOINing A的条件为A.b_id = B.id,但是C上的JOINing 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;
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到这个:http://sqlfiddle.com/#!2/dd462/3