左外连接与Where子句

时间:2015-03-19 23:22:03

标签: join outer-join

我对Access和SQL Server SSMS大约12个月有经验。

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

我有表1(左侧)有600k产品 我有表2,包含150,000种产品(表1的子集)。

当我这样做时

SELECT [Product_Code], [Product_Desc], Store
  FROM [Product Range] 

我得到600,000条记录

当我像这样进行左连接时

    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 

我获得了500k的记录。但我很困惑。我认为左边的连接应该从我左边的桌子上找回所有记录而不管其他什么。

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

         SELECT [Product_Code],[Product_Desc],r.store,soh.SOH
           FROM [Product Range] as r
LEFT OUTER JOIN [dbo].SOH as soh on r.[Product_Code] = soh.PRODUCT_Code 
            AND r.store = soh.store
          WHERE soh.CalYearWeek=1512  or soh.CalYearWeek is null

我获得了550,000条记录 - 仍然不是完整的600k。

我完全糊涂了,不知道出了什么问题。任何人都可以帮助我: - )

马特

3 个答案:

答案 0 :(得分:2)

问题是{<1}}条件在连接完成后执行,因此WHERE仅对成功连接才为真 - 错过的连接具有所有空值,并且where子句过滤掉它们。

解决方案很简单:将条件移动到 join

soh.CalYearWeek=1512

连接的条件在进行连接时执行,因此您仍然可以获得左连接,但仅限于右表中具有该特殊条件的行。

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 AND soh.CalYearWeek=1512 子句中的右表上放置非空条件有效地将LEFT连接转换为INNER连接,因为如果连接成功,右表只能具有非空值。

答案 1 :(得分:0)

你是正确的,因为没有WHERE子句的基本左连接将为LEFT表中的所有记录返回一行,其中RIGHT表存在时为RIGHT表,或者不存在时为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

然后你会看到返回的600k记录。 但是你要删除100k记录,其中soh.CalYearWeek不是1512行:

WHERE soh.CalYearWeek=1512

添加:

or soh.CalYearWeek is null

你正在追加50k以上的记录。所以基本上,WHERE子句在那时(在连接发生之后)对整个记录集起作用并过滤掉不匹配的行。在where子句中提到RIGHTTABLE.COLUMN实际上只是因为到那时,整行中的列由该完整标识符而不仅仅是其列名称来描述。

答案 2 :(得分:0)

实际上问题不在WHERE子句中。如果您可以将此问题称为问题,则问题在于JOIN本身以及它的行为方式。实际上,您可以获得正好600K行,根本没有行,少于600K行甚至超过600K行。它取决于这些表中的数据。

您应该理解将谓词置于JOIN条件和WHERE子句之间的区别。有一个很大的不同。您还应该了解谓词如何与NULL一起使用。

如果你有一行代码&#39; A&#39;在左表中,没有代码行&#39; A&#39;在右表中,您将从左表中获取一行,从右表中获取NULL。如果在右表中,您有一行代码&#39; A&#39;你将从左边一行,从右边一行。如果您有N行代码&#39; A&#39;在左表和M行中,代码为&#39; A&#39;在右边一个中,结果会得到M*N行。

总结这里是使用LEFT JOIN时计算结果集中行数的公式:

COUNT = Count of rows from left table where there are no corresponding rows from right table + SUM(COUNT(code[i])*COUNT(code[i])),即两个表中不同匹配代码计数的笛卡尔乘积之和。

左连接后至少有600K行。在year列中,您可以通过两种方式获取NULL:1。右表中的代码没有对应的行,2。右表中有相应的行,但是列年本身为NULL。

当您使用soh.CalYearWeek=1512进一步过滤结果集时,将从结果中删除具有NULL和不同值的行。

考虑例子:

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)