SQL关联查询:当采购记录符合条件时获取客户全部采购记录
解决方法:获取有指定采购记录客户的全部采购数据
你的问题很典型——原SQL的写法是在LEFT JOIN的关联条件里加了日期过滤,这会导致只有符合日期的采购记录会被匹配上,其他采购记录直接被排除了,自然只能看到单条符合条件的记录。要实现“只要客户有任意一条采购符合条件,就返回该客户所有采购记录”的需求,有几种常用的方案:
方案一:用子查询筛选目标客户ID
先找出所有在指定日期有采购的客户,再关联这些客户的全部采购记录:
SELECT c.*, p.* FROM customer c JOIN purchase p ON c.id = p.customer_id WHERE c.id IN ( -- 先拿到所有在2019-02-01有采购的客户ID SELECT customer_id FROM purchase WHERE purchase_date = '2019-02-01' )
如果需要保留客户信息(即使该客户除了目标日期外没有其他采购记录),可以把JOIN改成LEFT JOIN,不过这种情况返回的采购字段会是NULL,你可以根据实际需求调整。
方案二:用EXISTS子句(性能更优)
EXISTS子查询会在找到第一个匹配项后立即停止检索,在数据量较大时性能比IN更好:
SELECT c.*, p.* FROM customer c JOIN purchase p ON c.id = p.customer_id WHERE EXISTS ( SELECT 1 FROM purchase p2 WHERE p2.customer_id = c.id AND p2.purchase_date = '2019-02-01' )
这里的SELECT 1只是用来确认存在性,不需要返回具体字段,所以效率很高。
方案三:窗口函数(适合复杂场景)
如果你的数据库支持窗口函数(比如MySQL 8.0+、PostgreSQL、SQL Server等),可以用窗口函数标记客户是否有目标采购记录,再过滤出符合条件的行:
SELECT * FROM ( SELECT c.*, p.*, -- 按客户分组,标记该客户是否有符合日期的采购 MAX(CASE WHEN p.purchase_date = '2019-02-01' THEN 1 ELSE 0 END) OVER (PARTITION BY c.id) AS has_target_purchase FROM customer c LEFT JOIN purchase p ON c.id = p.customer_id ) sub_query WHERE has_target_purchase = 1
这种方法的优势是可以在一次扫描中完成标记和过滤,适合需要额外计算客户维度指标的复杂场景。
为什么原SQL不行?
再补充说明下原SQL的问题:你在LEFT JOIN的ON条件里加了purchase.purchase_date = '2019-02-01',这会让数据库只把客户和符合该日期的采购记录关联,其他采购记录不会被匹配,最终要么返回客户+符合条件的采购,要么返回客户+NULL(如果该客户没有符合日期的采购),完全达不到“返回客户全部采购”的目的。
内容的提问来源于stack exchange,提问作者david moeller




