订阅更新

销售分析二:买了 S8 却没买 iPhone 的买家

SQL 实战 123 题 · 第 56 题 简单 约 2 分钟读完

LeetCode 1083 · 条件聚合、「有 A 无 B」

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

「买过 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」的作用。
刷题进度保存在当前浏览器