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'
有人能指出一些文件来描述为什么会这样吗?非常感谢你
答案 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结果返回的内容。