基于AdventureWorks2017,查询同时有指定前后时间段订单的客户姓名的SQL问题
解决AdventureWorks中筛选跨时间段订单客户的问题
让我们先拆解你遇到的问题,然后一步步给出正确的解决方案:
为什么初始查询返回0行?
你的初始查询存在一个核心逻辑错误:你试图让同一个sales_order_table的OrderDate同时满足<= '2012-09-30'和>= '2013-06-30'——一个日期不可能同时早于2012年9月又晚于2013年6月,这种关联条件永远无法成立,自然返回0行数据。
修改后查询的边界问题
你后续的查询用NOT BETWEEN+COUNT(*) >=2的方式,会错误地包含那些仅在单个时间段有多次订单的客户(比如2012年前有3笔订单,2013年后没有),这不符合你"同时在两个时间段都有订单"的核心需求。
正确的解决方案
这里提供两种可靠的实现方式,按需选择:
方法1:使用EXISTS子查询(逻辑直观,易维护)
直接检查每个客户是否同时存在两个时间段的订单:
SELECT DISTINCT p.FirstName, p.MiddleName, p.LastName FROM Person.Person p JOIN Sales.Customer c ON p.BusinessEntityID = c.PersonID WHERE EXISTS ( -- 检查客户是否有2012-09-30之前的订单 SELECT 1 FROM Sales.SalesOrderHeader soh WHERE soh.CustomerID = c.CustomerID AND soh.OrderDate <= '2012-09-30' ) AND EXISTS ( -- 检查客户是否有2013-09-30之后的订单 SELECT 1 FROM Sales.SalesOrderHeader soh WHERE soh.CustomerID = c.CustomerID AND soh.OrderDate >= '2013-09-30' );
方法2:使用分组+条件聚合(大数据量下性能更优)
通过一次扫描订单表,统计每个客户在两个时间段的订单数,确保每个时间段至少有1笔:
SELECT p.FirstName, p.MiddleName, p.LastName FROM Person.Person p JOIN Sales.Customer c ON p.BusinessEntityID = c.PersonID JOIN Sales.SalesOrderHeader soh ON c.CustomerID = soh.CustomerID -- 用BusinessEntityID分组更可靠,避免同名不同人被错误合并 GROUP BY p.BusinessEntityID, p.FirstName, p.MiddleName, p.LastName HAVING -- 统计2012-09-30前的订单数,至少1笔 COUNT(CASE WHEN soh.OrderDate <= '2012-09-30' THEN 1 END) >= 1 -- 统计2013-09-30后的订单数,至少1笔 AND COUNT(CASE WHEN soh.OrderDate >= '2013-09-30' THEN 1 END) >= 1;
注意事项
- 你原始需求中是
2013-09-30之后,但修改版查询写的是2013-06-30,请确保使用符合业务需求的日期值。 - 分组时优先使用
BusinessEntityID(唯一标识)而不是仅用姓名,避免同名客户被错误合并。
内容的提问来源于stack exchange,提问作者Lathezar Mitev
相关产品推荐
相关产品推荐

