左连接变成内连接

时间:2010-07-15 14:07:03

标签: sql

SELECT
a.foo
b.bar
c.foobar
FROM tableOne AS a
INNER JOIN tableTwo AS b ON a.pk = b.fk
LEFT JOIN tableThree AS c ON b.pk = c.fk
WHERE a.foo = 'something'
AND c.foobar = 'somethingelse'

在where子句之后使用和子句似乎将左连接转换为内连接。我看到的行为是,如果tableThree中没有'somethingelse',将返回0行。

如果我将c.foobar ='somethingelse'移动到join子句中,则存储的连接将像左连接一样运行。

    SELECT
    a.foo
    b.bar
    c.foobar
    FROM tableOne AS a
    INNER JOIN tableTwo AS b ON a.pk = b.fk
    LEFT JOIN tableThree AS c ON b.pk = c.fk
    AND c.foobar = 'somethingelse'
    WHERE a.foo = 'something'

有人能指出一些文件来描述为什么会这样吗?非常感谢你

8 个答案:

答案 0 :(得分:41)

这是因为你的WHERE条款。

每当您在WHERE子句(NOT NULL)中指定左连接右侧的值时,您必须消除所有NULL值并且它基本上变为INNER JOIN

如果你写,AND (c.foobar = 'somethingelse' OR c.foobar IS NULL)将解决问题。

您还可以将c.foobar部分移动到您的连接谓词中,这也可以解决问题。

答案 1 :(得分:3)

您看到这个的原因是因为左连接将c的所有列设置为NULL,以用于c中不存在的那些行(即无法连接的行)。这意味着比较c.foobar = 'somethingelse'不正确,这就是为什么不返回这些行的原因。

如果将c.foobar = 'somethingelse'移动到连接条件,则当条件不为真时,该连接仍然会返回这些行(尽管是NULL值)。

答案 2 :(得分:1)

加入后执行'where'子句。这对于内部连接无关紧要,但对外部连接很重要。

缩短示例

SELECT b.bar,c.foobar FROM tableTwo AS b LEFT JOIN tableThree AS c ON b.pk=c.fk WHERE c.foobar='somethingelse'

Raw data                  Outer Join Result        Select Result
b.pk c.fk c.foorbar       b.pk c.fk c.foorbar      c.foorbar
1    1    something       1    1    something      <not in result set>
1    1    somethingelse   1    1    somethingelse  somethingelse


SELECT b.bar,c.foobar FROM tableTwo AS b LEFT JOIN tableThree AS c ON b.pk=c.fk AND c.foobar='somethingelse'

Raw data                  Outer Join Result        Select Result
b.pk c.fk c.foorbar       b.pk c.fk c.foorbar      c.foorbar
1    1    something       1    null null           null
1    1    somethingelse   1    1    somethingelse  somethingelse

答案 3 :(得分:0)

联接正在进行他们的工作,然后在哪里删除c.foobar&lt;&gt;的记录'somethingelse'。

效果看起来像是内连接,但实际上并非如此。

答案 4 :(得分:0)

左连接返回左表(示例中的tableTwo)中的所有内容以及右侧表中的任何匹配行(示例中为tableThree)。当您对左连接右侧的某些内容进行过滤(即tableThree)并且您没有考虑不匹配的值时,您实际上要求存在一个值,并且值为'something',它相当于一个内部加入。如果你要做的是找到tableThree中没有行的所有tableTwo行,其foobar值为'something',你可以将过滤移动到on子句中:

Select a.foo, b.bar, c.foobar
From tableOne As a
    Inner Join tableTwo as b
        On b.fk = a.pk
    Left Join tableThree as c
        On c.fk = b.pk
            And c.foobar = 'something'
Where a.foo = 'something'
    And c.pk Is Null

最后添加,c.pk Is Null过滤掉没有tableThree值且foobar值为'something'的值。如果只想看foTar值为'something'时的tableThree值(和否则为null),然后删除我添加的c.pk Is Null的其他过滤器。

答案 5 :(得分:0)

第一种情况(,其中c.foobar位于where子句中)c.foobar的过滤发生在连接发生后(它们正确发生 ),所以你有效地过滤掉那里没有'somethingelse'的所有结果记录。

第二种情况c.foobar是连接的一部分,因此它在加入时计算并仅控制连接的输出,而不是最终记录集(返回连接失败的null)。

答案 6 :(得分:0)

LEFT JOIN在没有匹配行的情况下产生NULL。在这种情况下,对于不匹配的行,c.foobar将为NULL。但是你的WHERE子句正在寻找一个特定的值:'somethingelse',因此将过滤掉所有的NULL值。由于INNER JOIN在右侧也没有产生NULL值,因此两者看起来相同。您可以添加'OR c.foobar IS NULL'以允许返回空值。

将条件移动到ON子句时,它将成为JOIN行匹配的一部分,而不是最终的过滤器。连接匹配可能会失败,然后外部连接在“c.foobar”为NULL或不是“somethingelse”的情况下返回NULL。

答案 7 :(得分:0)

它没有将LEFT JOIN变成INNER JOIN,认为效果可能看起来是相同的。 设置WHERE条件时

  

AND c.foobar ='somethingelse'

你正在摆脱所有允许LEFT JOIN行动的案例。在这种情况下,c.foobar的某些值将为NULL。在JOIN条件下设置此选项仍允许不匹配的LEFT JOIN结果,仅限制为C结果返回的内容。