左外连接与 Where 子句

Left outer join with Where Clause

我对 Access 很有经验,并且在 SQL Server SSMS 方面有大约 12 个月的经验。

我没有得到我期望的左外连接结果,我不知道为什么。可能是我没看懂

我有 Table 1 个(左侧)有 600k 个产品 我有 table 2 和 150,000 个产品(table 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 

我有 50 万条记录。但我很困惑。我认为左连接应该 return 我左边的所有记录 table 而不管其他任何事情。

然后我尝试了这个(我不知道为什么我仍然需要添加 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。

我一头雾水,不知道哪里出了问题。谁能帮帮我:-)

马特

你是正确的,没有 WHERE 子句的基本左连接将 return LEFT 中所有记录的行 table 与 RIGHT table 中的任一数据它存在,或者在它不存在的地方为 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 万条记录 returned。 但是然后你要删除 soh.CalYearWeek 不是 1512 的 100k 记录,行:

WHERE soh.CalYearWeek=1512

通过添加:

or soh.CalYearWeek is null

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

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

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

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

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

将非空条件放在 WHERE 子句的右侧 table 有效地将 LEFT 连接转换为 INNER 连接,因为右侧 table 只能有一个非空连接如果加入成功则为空值。

其实问题不在WHERE子句。问题,如果你可以称之为问题的话,是在 JOIN 本身及其行为方式中。事实上,您可以获得恰好 600K 行,根本没有行,少于 600K 行或什至超过 600K 行。这取决于那些 table 中的数据。

您应该了解将谓词放在 JOIN 条件和 WHERE 子句中的区别。有一个很大的不同。您还应该了解谓词如何与 NULLs.

一起使用

如果左侧 table 有代码 'A' 的行,右侧 table 没有代码 'A' 的行,您将从左侧 table 和右边的 NULL table。如果在右侧 table 中有一行代码为 'A',您将从左侧获得 1 行,从右侧获得 1 行。如果您有 N 行代码 'A' 在左侧 table 和 M 行代码 'A' 在右侧,您将得到 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])),即来自两个 table 的不同匹配代码计数的笛卡尔积之和。

左联接后您至少得到 600K 行。在 year 列中,您可以通过两种方式获取 NULL:1. 右侧 table 中没有相应的代码行, 2. 右侧 table 中有相应的行,但年份列为 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

现在不同的结果取决于秒中的数据 table:

--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)