为啥以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制]
Posted
技术标签:
【中文标题】为啥以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制]【英文标题】:Why and when a LEFT JOIN with condition in WHERE clause is not equivalent to the same LEFT JOIN in ON? [duplicate]为什么以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制] 【发布时间】:2013-03-20 07:46:19 【问题描述】:我遇到了一个非常令人困惑的情况,这让我质疑我对 SQL Server 中联接的所有理解。
SELECT t1.f2
FROM t1
LEFT JOIN t2
ON t1.f1 = t2.f1 AND cond2 AND t2.f3 > something
与以下结果不同:
SELECT t1.f2
FROM t1
LEFT JOIN t2
ON t1.f1 = t2.f1 AND cond2
WHERE t2.f3 > something
能否请人帮忙判断这两个查询是否应该相等?
谢谢
【问题讨论】:
ON
减少了连接的行集,而WHERE
减少了添加到结果集中的行集...
它们不等价。您的第一个查询过滤了联接中的结果;第二个过滤器在之后。
我不确定问题是否等价,因为这里的条件在第二个表上表达..
【参考方案1】:
on
子句在join
查找匹配行时使用。 where
子句用于在所有连接完成后过滤行。
迪士尼卡通人物投票选举总统的例子:
declare @candidates table (name varchar(50));
insert @candidates values
('Obama'),
('Romney');
declare @votes table (voter varchar(50), voted_for varchar(50));
insert @votes values
('Mickey Mouse', 'Romney'),
('Donald Duck', 'Obama');
select *
from @candidates c
left join
@votes v
on c.name = v.voted_for
and v.voter = 'Donald Duck'
即使Donald
没有投票给他,这仍然返回Romney
。如果将条件从 on
移动到 where
子句:
select *
from @candidates c
left join
@votes v
on c.name = v.voted_for
where v.voter = 'Donald Duck'
Romney
将不再出现在结果集中。
【讨论】:
请注意,此示例使用的是 OUTER 连接,在这种情况下,这 2 个代码 sn-ps 在功能上并不等效。 我一直在寻找完全不同的东西,但不知何故谷歌把我带到了这里,你的拳头两句话拯救了我的一天!谢谢+1!【参考方案2】:两者在字面上是不同的。
第一个查询在表连接之前过滤表t2
。因此结果将被连接到表t1
上,结果t1
的所有记录将显示在列表中。
第二个在加入表格后从总结果中过滤。
这是一个例子
表1
ID Name
1 Stack
2 Over
3 Flow
表2
T1_ID Score
1 10
2 20
3 30
在您的第一个查询中,它看起来像这样,
SELECT a.*, b.Score
FROM Table1 a
LEFT JOIN Table2 b
ON a.ID = b.T1_ID AND
b.Score >= 20
它的作用是在加入表格之前,首先通过分数过滤table2
的记录。因此,将在 table1 上连接的唯一记录是
T1_ID Score
2 20
3 30
SQLFiddle Demo
因为T1_ID
的Score
只有10个。查询的结果是
ID Name Score
1 Stack NULL
2 Over 20
3 Flow 30
SQLFiddle Demo
虽然第二个查询不同。
SELECT a.*, b.Score
FROM Table1 a
LEFT JOIN Table2 b
ON a.ID = b.T1_ID
WHERE b.Score >= 20
它首先连接记录,无论它在另一个表上是否有匹配的记录。所以结果会是
ID Name Score
1 Stack 10
2 Over 20
3 Flow 30
SQLFiddle Demo
过滤发生在b.Score >= 20
。所以最终的结果是
ID Name Score
2 Over 20
3 Flow 30
SQLFiddle Demo
【讨论】:
解释得很好!【参考方案3】:在第一种情况下,t2
中的结果作为连接的一部分被过滤。
在第二种情况下,t2
可能有更多行可用。
本质上,两个查询中加入的记录集不会相同。
【讨论】:
【参考方案4】:这确实有所不同,因为在第二种情况下,您在执行左连接之后应用 where
【讨论】:
【参考方案5】:CREATE TABLE Company
(
CompanyId TinyInt Identity Primary Key,
CompanyName Nvarchar(50) NULL
)
GO
INSERT Company VALUES('DELL')
INSERT Company VALUES('HP')
INSERT Company VALUES('IBM')
INSERT Company VALUES('Microsoft')
GO
CREATE TABLE Candidate
(
CandidateId tinyint identity primary key,
FullName nvarchar(50) NULL,
CompanyId tinyint REFERENCES Company(CompanyId)
)
GO
INSERT Candidate VALUES('Ron',1)
INSERT Candidate VALUES('Pete',2)
INSERT Candidate VALUES('Steve',3)
INSERT Candidate VALUES('Steve',NULL)
INSERT Candidate VALUES('Ravi',1)
INSERT Candidate VALUES('Raj',3)
INSERT Candidate VALUES('Kiran',NULL)
GO
SELECT * from Company c
SELECT * from Candidate c
-- A simple left outer Join
SELECT * FROM Company c LEFT OUTER JOIN Candidate c2
ON c.CompanyId = c2.CompanyId
--Left Outer Join ON and AND condition fetches 5 rows wtih NULL value from right side table
SELECT * FROM Company c LEFT OUTER JOIN Candidate c2
ON c.CompanyId = c2.CompanyId
AND c.CompanyName = 'DELL'
--Left Outer Join ON and where clause fetches only required rows
SELECT * FROM Company c LEFT OUTER JOIN Candidate c2
ON c.CompanyId = c2.CompanyId
AND c.CompanyName = 'DELL'
WHERE c.CompanyName='IBM'
【讨论】:
以上是关于为啥以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
在 from 子句 *and* where 子句中添加连接条件使查询更快。为啥?
Laravel 5.8,在 where 子句中带有 Count(*) 的棘手子查询
MySQL:在 WHERE 子句中带有 NOT IN 的从属子查询非常慢