27

我遇到了一个非常令人困惑的情况,这让我质疑我对 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 

请有人帮忙告诉这两个查询是否应该等效?

谢谢

4

5 回答 5

39

当查找匹配行时使用该on子句。joinwhere子句用于在所有连接完成后过滤行。

迪士尼卡通人物投票选举总统的例子:

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'

Romney即使Donald没有投票给他,这仍然会返回。如果将条件从子句on移至where子句:

select  *
from    @candidates c
left join    
        @votes v
on      c.name = v.voted_for
where   v.voter = 'Donald Duck'

Romney将不再在结果集中。

于 2013-03-29T15:29:53.307 回答
22

两者在字面上是不同的。

第一个查询在表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

因为ScoreofT1_ID只有10。查询的结果是

ID   Name    Score
1    Stack   NULL
2    Over    20
3    Flow    30

而第二个查询不同。

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

和过滤发生b.Score >= 20。所以最终的结果将是

ID   Name    Score
2    Over    20
3    Flow    30
于 2013-03-29T15:24:16.610 回答
1
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'
于 2013-03-29T15:41:47.840 回答
0

在第一种情况下,结果t2将作为连接的一部分进行过滤。

在第二种情况下,可能会有更多行来自t2.

本质上,在两个查询中加入的记录集是不一样的。

于 2013-03-29T15:21:19.643 回答
0

它确实有所作为,因为在第二种情况下,您在执行左连接之后应用 where

于 2013-03-29T15:22:00.017 回答