在TransactSQL中离开加入会给我带来意想不到的结果 - 我对左连接的理解是错误的吗?

时间:2011-01-14 19:28:11

标签: sql sql-server tsql

这是我的数据

CREATE TABLE TempA (  
  ID  INT IDENTITY(1,1),  
  Msg  VARCHAR(20)  
)  

INSERT INTO TempA (Msg) values ( 'a')  
INSERT INTO TempA (Msg) values ( 'b')  
INSERT INTO TempA (Msg) values ( 'c')  

CREATE TABLE TempB (  
  ID  INT IDENTITY(1,1),  
  Msg  VARCHAR(20)  
)  

所以TempB是空的。现在我运行以下查询

   select a.* 
     from TempA a 
left JOIN TempA B on a.id = b.id  

它按预期从TempA返回3行,到目前为止还不错。让我们在上面的查询中添加一个过滤器

   select a.* 
     from TempA a 
left JOIN TempA B on a.id = b.id  
    where b.msg = 'aa'  

它不向我返回任何行。我认为,因为它是一个左连接,我仍然应该从TempA表获得3行。我错了吗?

5 个答案:

答案 0 :(得分:4)

您正在通过在b.msg子句使用中包含where过滤器将左连接转换为内连接。

select a.* from TempA a left JOIN TempB B on a.id = b.id and b.msg = 'aa' 

(从概念上讲)加入谓词发生时,来自A的非加入行会重新加入,NULL的值为b.msg,然后您再次使用过滤器排除这些行!

您可能想要查看Itzik Ben Gan的Logical Query Processing Poster

答案 1 :(得分:4)

这是正在发生的事情;

你有两张桌子。

TempA ID Msg
      -- ---
      1  a
      2  b
      3  c

TempB ID Msg
      -- ---

现在,当您进行连接时,初始结果如下所示:

Result a.ID a.Msg b.ID b.Msg
       ---- ----- ---- -----
       1    a     NULL NULL
       2    b     NULL NULL
       3    c     NULL NULL

当您使用WHERE子句过滤该查询时,您将过滤掉b.Msg 'aa' b.Msg的任何内容。这会过滤掉所有记录,因为所有记录都有NULL Result a.ID a.Msg b.ID b.Msg ---- ----- ---- ----- 。离开你:

TempA

然后,您只选择Result a.ID a.Msg ---- ----- 中的列,这是最终结果:

{{1}}

答案 2 :(得分:2)

使用OUTER JOIN时,WHERE子句中提供的条件在 JOIN后应用 。因为没有b.msg ='aa'的行,所以不会返回任何行。

ON子句中指定条件后,条件将在 JOIN之前应用,因此对b的引用只会是影响。在此示例中,b引用将返回NULL,而a引用不受影响。

答案 3 :(得分:1)

我更喜欢马丁的解决方案,但另一个选择是

select a.* 
from 
    TempA a 
    left JOIN TempB B on a.id = b.id
where b.msg = 'aa' or b.msg is null

答案 4 :(得分:0)

这取决于你如何进行过滤 几个月前我回答了类似的问题。看看我关于左连接的解释:

What is the difference in these two queries as getting two different result set?