Join 带Where子句的左外连接

Join 带Where子句的左外连接,join,outer-join,Join,Outer Join,我对Access很有经验,在SQL Server SSMS中使用了大约12个月 我没有得到我所期望的左外连接的结果,我不知道为什么。也许我不明白什么 我有表1(左侧)和600k产品 我有150000个产品的表2(表1的子集) 当我这么做的时候 SELECT [Product_Code], [Product_Desc], Store FROM [Product Range] 我有60万张唱片 当我做这样的左连接时 SELECT [Product_Code], [Product_De

我对Access很有经验,在SQL Server SSMS中使用了大约12个月

我没有得到我所期望的左外连接的结果,我不知道为什么。也许我不明白什么

我有表1(左侧)和600k产品 我有150000个产品的表2(表1的子集)

当我这么做的时候

SELECT [Product_Code], [Product_Desc], Store
  FROM [Product Range] 
我有60万张唱片

当我做这样的左连接时

    SELECT [Product_Code], [Product_Desc], r.store, soh.SOH
      FROM [Product Range] as r
 LEFT JOIN [dbo].SOH as soh on r.[Product_Code] = soh.PRODUCT_Code 
       AND r.store = soh.store
      WHERE soh.CalYearWeek=1512 
我有50万张唱片。但是我很困惑。我认为左联接应该返回我左表中的所有记录,而不考虑其他任何内容

然后我尝试了这个(我不知道为什么我需要添加空条件)

我有55万张唱片,但还没有全部60万张

我完全糊涂了,不知道怎么了。有人能帮我吗:-)


Matt

您是正确的,没有WHERE子句的基本左联接将为左表中的所有记录返回一行,其中右表的数据(如果存在)或NULL(如果不存在)

这就是你得到的,但是你添加了一个WHERE子句,它将过滤掉某些行。因此,如果你刚刚有:

SELECT [Product_Code] ,[Product_Desc] ,r.store ,soh.SOH
FROM [Product Range] as r left join [dbo].SOH as soh 
                          on r.[Product_Code] = soh.PRODUCT_Code 
                             and r.store = soh.store
那么您将看到返回的60万条记录。 但随后您将删除soh.CalYearWeek不是1512的10万条记录,行为:

WHERE soh.CalYearWeek=1512
加入:

or soh.CalYearWeek is null

您将添加回5万条以上的记录。所以基本上,WHERE子句在当时(连接发生后)作用于整个记录集,并过滤掉不匹配的行。在where子句中提到RIGHTTABLE.COLUMN实际上只是因为到那时,整行中的列由该完整标识符描述,而不仅仅是其列名。

没有WHERE子句的基本左联接将为左表中的所有记录返回一行,其中右表的数据(如果存在)或空(如果不存在)

这就是你得到的,但是你添加了一个WHERE子句,它将过滤掉某些行。因此,如果你刚刚有:

SELECT [Product_Code] ,[Product_Desc] ,r.store ,soh.SOH
FROM [Product Range] as r left join [dbo].SOH as soh 
                          on r.[Product_Code] = soh.PRODUCT_Code 
                             and r.store = soh.store
那么您将看到返回的60万条记录。 但随后您将删除soh.CalYearWeek不是1512的10万条记录,行为:

WHERE soh.CalYearWeek=1512
加入:

or soh.CalYearWeek is null

您将添加回5万条以上的记录。所以基本上,WHERE子句在当时(连接发生后)作用于整个记录集,并过滤掉不匹配的行。where子句中提到RIGHTTABLE.COLUMN实际上只是因为到那时,整行中的列由该完整标识符描述,而不仅仅是它的列名。

问题在于,在进行连接后执行了
where
条件,因此,
soh.CalYearWeek=1512
仅对成功的联接为真-丢失的联接具有所有空值,where子句会将它们过滤掉

解决方案很简单:将条件移动到联接中:

连接上的条件是在进行连接时执行的,因此您仍将获得左侧连接,但仅限于右表中具有该特殊条件的行


WHERE
子句的右表上放置非空条件有效地将左连接转换为内部连接,因为只有在连接成功时,右表才能具有非空值。

问题在于,在连接完成后执行
WHERE
条件,因此,
soh.CalYearWeek=1512
仅对成功的联接为真-丢失的联接具有所有空值,where子句会将它们过滤掉

解决方案很简单:将条件移动到联接中:

连接上的条件是在进行连接时执行的,因此您仍将获得左侧连接,但仅限于右表中具有该特殊条件的行


WHERE
子句的右表上放置非空条件有效地将左连接转换为内部连接,因为只有在连接成功时,右表才能具有非空值。

事实上,问题不在
WHERE
子句中。如果可以称之为问题的话,问题在于
JOIN
本身及其行为。事实上,您可以得到正好600K行,完全没有行,少于600K行,甚至超过600K行。这取决于这些表中的数据

您应该理解将谓词放入
JOIN
条件和
WHERE
子句之间的区别。这有很大的区别。您还应该了解谓词如何与
NULL
s一起工作

若在左表中有一行代码为“a”,而在右表中并没有代码为“a”的行,那个么将从左表中得到一行,从右表中得到空值。若在右边的表格中有一行代码为“A”,则从左边得到一行,从右边得到一行。如果左表中有
N
行代码为'A',右表中有
M
行代码为'A',则结果中会出现
M*N

这里总结一下使用
左联接时计算结果集中行数的公式:

COUNT=左表中没有对应行的行数+和(COUNT(code[i])*COUNT(code[i]))
,即两个表中不同匹配代码的计数的笛卡尔乘积之和

在左联接之后,您至少可以得到600K行。在
year
列中,可以通过两种方式获取空值:1。右表2中的代码没有对应的行。右表中有对应的行,但列year本身为空

当您使用
soh.CalYearWeek=1512进一步筛选结果集时,结果中将删除带有空值和不同值的行

举个例子:

DECLARE @t1 TABLE(Code INT)
DECLARE @t2 TABLE(Code INT, Year INT)

INSERT INTO @t1 VALUES
(1), (2), (3)

SELECT * FROM @t1 t1
JOIN @t2 t2 ON t2.Code = t1.Code
WHERE t2.Year = 1512
现在根据第二个表中的数据得出不同的结果:

--count 1
INSERT INTO @t2 VALUES
(1, 1512)

--count 0
INSERT INTO @t2 VALUES
(1, NULL)

--count 3
INSERT INTO @t2 VALUES
(1, 1512), (1, 1512), (1, 1512)

--count 6
INSERT INTO @t2 VALUES
(1, 1512), (2, 1512), (2, 1512), (3, 1512), (3, 1512), (3, 1512)

事实上,问题不在
WHERE
子句中。如果你可以称之为问题,那么问题就在
JOIN
its中