只能用 group by + having 筛「只买过某品类」用户:需同时满足 count(distinct category) = 1 且 min(category) = 'electronics',并提前在 where 中过滤 null 和空字符串,否则结果错误。

用 GROUP BY + HAVING 筛出「只买过某品类」的用户
直接结论:不能只靠 GROUP BY,必须搭配 HAVING 对分组后的聚合结果做条件过滤,且要排除其他品类的存在。常见错误是只写 HAVING MAX(category) = 'electronics' —— 这只能保证「至少买过一次 electronics」,完全不管用户是否还买过 books 或 clothing。
正确写法:用 COUNT(DISTINCT category) = 1 且 MIN/MAX 同值
核心逻辑是:一个用户只买过特定品类 → 他所有订单的 category 值唯一,且等于目标品类。需同时满足两个条件:
-
COUNT(DISTINCT category) = 1(确保没混买) -
MIN(category) = 'electronics'或MAX(category) = 'electronics'(确保那个唯一品类就是目标值)
示例(查只买过 'electronics' 的用户):
SELECT user_id FROM orders GROUP BY user_id HAVING COUNT(DISTINCT category) = 1 AND MIN(category) = 'electronics';
遇到 NULL 或空字符串时要显式过滤
如果 category 字段允许为 NULL 或空字符串(''),上面的 COUNT(DISTINCT category) 会把 NULL 当作一个独立值计入,导致误判。务必在 WHERE 子句提前清理:
- 加
WHERE category IS NOT NULL AND category != '' - 否则,一个用户有 1 条
electronics订单 + 1 条NULL订单,COUNT(DISTINCT category)会算出 2,但实际你可能想忽略NULL订单
修正后完整语句:
SELECT user_id FROM orders WHERE category IS NOT NULL AND category != '' GROUP BY user_id HAVING COUNT(DISTINCT category) = 1 AND MIN(category) = 'electronics';
性能注意:GROUP BY user_id 在大表上可能慢
如果 orders 表超千万行,GROUP BY user_id 本身不慢,但 COUNT(DISTINCT category) 是高成本操作,尤其当每个用户订单数多、品类分散时。可考虑:
- 给
(user_id, category)建联合索引,加速分组和去重 - 若只需判断「是否只买过 A」,不用真算去重数量,可用
SUM(CASE WHEN category != 'electronics' THEN 1 ELSE 0 END) = 0替代 —— 这个表达式能走索引且避免 DISTINCT 开销
替代写法(更高效,但语义稍绕):
SELECT user_id FROM orders WHERE category IS NOT NULL AND category != '' GROUP BY user_id HAVING SUM(CASE WHEN category = 'electronics' THEN 0 ELSE 1 END) = 0;
这个 HAVING 的意思是:对每个用户,统计他所有非 'electronics' 的订单数,为 0 才保留 —— 本质等价,但数据库优化器更容易利用索引。
真正容易被忽略的是:业务上「只购买过特定品类」往往隐含时间范围(比如近 90 天),而 SQL 里漏加 WHERE order_date >= ... 会导致结果跨周期失真。










