销售分析二:买了 S8 却没买 iPhone 的买家
LeetCode 1083 · 条件聚合、「有 A 无 B」
面试怎么回答
「买过 A 没买过 B」我用分组条件计数:按买家分组,HAVING SUM(product_name = 'S8') > 0 AND SUM(product_name = 'iPhone') = 0。这类判断必须在分组层面做,单行的 WHERE 无法表达「从没买过」。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
销售表记录买家买了什么产品,产品表有产品名称。找出买过 S8、但从没买过 iPhone 的买家。
表结构
- Product:product_id(主键)、product_name、unit_price
- Sales:seller_id、product_id、buyer_id、sale_date、quantity、price
思路
「有 A 且无 B」最清晰的写法是按买家分组后做两个条件计数:买 S8 的次数 > 0,并且买 iPhone 的次数 = 0。原文第一种写法先只保留 S8 和 iPhone 两种产品,再挑出只买过其中一种、且这一种是 S8 的买家,结果一样但更绕。
参考解法
写法 1:只保留两种产品,挑出只买过一种且是 S8 的买家
select t.buyer_id from(
select s.buyer_id, p.product_name
from sales s
inner join
product p
on s.product_id=p.product_id and (p.product_name='S8' or p.product_name='iPhone')
group by s.buyer_id
having count(distinct p.product_name) = 1
) t
where t.product_name='S8'
写法 2:条件计数:S8 > 0 且 iPhone = 0(更直观)
select s.buyer_id
from sales as s left join product as p
on s.product_id=p.product_id
group by buyer_id
having sum(p.product_name='S8')>0 and sum(p.product_name='iPhone')=0
易错点
- 写成
WHERE product_name = 'S8' AND product_name != 'iPhone'是在单行上判断,永远等价于只判断 S8,起不到「没买过 iPhone」的作用。