为啥这个 LEFT OUTER JOIN 不包括左侧的所有主键

Posted

技术标签:

【中文标题】为啥这个 LEFT OUTER JOIN 不包括左侧的所有主键【英文标题】:Why this LEFT OUTER JOIN is not including all the Primary Keys from the Left为什么这个 LEFT OUTER JOIN 不包括左侧的所有主键 【发布时间】:2017-10-06 21:48:24 【问题描述】:

customers 表共有 1000 个客户,其中 1500 个在 2016 财年下订单。但我们希望显示所有客户及其在 2016 财年的订单总数,无论客户是否在该财年下订单。但 SQL Server 2012 中的以下查询仅显示 1490。

我们可能在这里遗漏了什么?

SELECT c.CustomerID, count(*) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
WHERE o.FiscalYear = '2016'
GROUP BY c.CustomerID

更新

以下查询仅返回 1 条记录 (1491) - 仍然缺少 9 条记录。

SELECT c.CustomerID, count(*) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o ON c.CustomerID = o.CustomerID
                   AND o.FiscalYear = '2016'
GROUP BY c.CustomerID

【问题讨论】:

连接后应用 where 子句。因此,连接为没有订单的客户返回空值。但由于这些客户没有订单,会计年不等于 2016 年,因此记录被排除在外。更正将与外部连接相关的限制标准移动到连接标准,以便与连接一起施加限制。 您的总记录数应为 1000(客户),总和应为 1500。总和小于 1500 的唯一可能差异是您删除了客户和孤立的订单。或者您对 2016 年 1500 个订单的计数一开始就是错误的。 SELECT * from orders where customerID not in (Select customerID from customers) and FiscalYear = '2016' 给你一些记录吗? 是的,我会开始检查以下内容:对于所有记录,您的 FiscalYear 实际上是不同的“2016”吗?有可能出现错别字吗?记录如何标记为已删除(位/整数标志)? select count(distinct customerid), count(*) from orders where fiscalyear = '2016' 返回什么? 如果没有键那么你有数字差异的原因是你有孤立的订单记录(客户记录已被删除,因此订单不能与客户绑定。你可以使用完整的外部加入而不是左键查看其中一些没有客户的记录。它应该让您的计数达到 1500。 【参考方案1】:

您的where 子句正在将left outer join 变成Inner join

改成AND:

SELECT c.CustomerID, count(o.CustomerID) AS TotalOrders
FROM Customers c
LEFT JOIN Orders o
    ON c.CustomerID = o.CustomerID
    AND o.FiscalYear = '2016'     -- Here
GROUP BY c.CustomerID

【讨论】:

我应该提到我曾尝试在 LEFT JOIN 中包含 Where 条件,但它只将总记录增加了 1。仍然缺少 9 个。 @nam 这不是查询。你能检查一下现在客户表中有多少不同的 CustomerID 吗? 查询中还有一个错误:COUNT(*) 应该是COUNT(o.CustomerID) 或类似的,以便对没有订单的客户计数 0,而不是 1。 @ThorstenKettner - 完全正确。更新。谢谢你。【参考方案2】:

正确的 SQL 是:

SELECT c.CustomerID, count(o.CustomerID) AS TotalOrders,
       sum(count(o.CustomerID)) over () as TotalTotalOrders
FROM Customers c LEFT JOIN
     Orders o
     ON c.CustomerID = o.CustomerID AND o.FiscalYear = '2016'
GROUP BY c.CustomerID;

TotalTotalOrders 应该是所有订单(或至少是具有有效客户 ID 的订单)。

【讨论】:

【参考方案3】:

这将列出所有客户,无论他们是否有任何订单无论下订单的年份是什么。然后sum 将计算 2016 年下的所有订单,忽略其余订单,并返回一个整数(即它永远不会为空)。

SELECT
   c.CustomerID
  ,sum(case when o.FiscalYear = '2016' then 1 else 0 end)  AS TotalOrders
FROM Customers c
LEFT JOIN Orders o
 ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerID

【讨论】:

嗯,它的性能不如@Gurwinder 的回答,但如果您的要求发生变化,它会提供更大的灵活性。

以上是关于为啥这个 LEFT OUTER JOIN 不包括左侧的所有主键的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 数据库中 left outer join 和 left join 啥区别

“,”“natural join”“natural left outer join”“natural right outer join”的用法总结

从 LEFT OUTER JOIN 中删除重复项

SQL 查询条件放在LEFT OUTER JOIN 的ON语句后与放在WHERE中的区别

SQL_连接(Join),内部连接(INNER JOIN),左连接(LEFT JOIN ),右连接(RIGHT JOIN)完整外部连接(FULL OUTER JOIN),自连接(Self JOIN)(

R语言merge函数左连接dataframe数据(Left (outer) join in R)左连接必须将参数all设置(all.x = TRUE)默认merge函数通过公共列名合并数据集