查只买过一种商品的用户需用 HAVING COUNT(DISTINCT product_id) = 1,因 COUNT(product_id) 统计次数而非种类数,易误包含复购同一商品的用户;GROUP BY user_id 是前提,DISTINCT 必须置于 COUNT() 内。

查只买过一种商品的用户,必须用 HAVING COUNT(DISTINCT product_id)
直接用 COUNT(product_id) 会漏掉重复下单同一商品的用户——比如用户A买了5次iPhone,COUNT 算出来是5,但实际只买过1种。必须用 COUNT(DISTINCT product_id) 才能统计“种类数”。
常见错误现象:HAVING COUNT(product_id) = 1 返回结果明显偏多,且包含大量高频复购同一商品的用户。
- GROUP BY 用户字段(如
user_id)是前提,没它HAVING无意义 -
DISTINCT必须写在COUNT()里面,写成COUNT DISTINCT product_id(缺括号)会报错 - MySQL 5.7+、PostgreSQL、SQL Server 都支持,但旧版 SQLite(DISTINCT 在聚合函数里,得换写法
MySQL 8.0+ 可用窗口函数避免 GROUP BY,但没必要
有人想绕开 GROUP BY,用 COUNT(DISTINCT product_id) OVER (PARTITION BY user_id) 算出每个用户的商品种类数,再外层过滤。可行,但多此一举:
- 窗口函数会扫描全表再计算,比带
GROUP BY的聚合查询更慢,尤其数据量大时 - 逻辑变复杂:需嵌套子查询或 CTE,可读性下降
- 如果只要用户ID,
GROUP BY + HAVING一行SELECT user_id FROM orders GROUP BY user_id HAVING COUNT(DISTINCT product_id) = 1最直白
注意 NULL 和空字符串对 COUNT(DISTINCT) 的影响
COUNT(DISTINCT product_id) 默认忽略 NULL 值——如果某订单 product_id 是 NULL,它不参与计数,也不导致整行被排除。这容易引发误判:
- 用户所有订单的
product_id全是NULL,COUNT(DISTINCT product_id)结果为 0,不会被= 1匹配到,这是对的 - 但若混有有效值和
NULL(如1个iPhone + 2个NULL),COUNT(DISTINCT)仍算作1,用户会被错误纳入结果 - 稳妥做法是加过滤:
WHERE product_id IS NOT NULL,或用COUNT(DISTINCT COALESCE(product_id, ''))显式把 NULL 当作一类(但通常不推荐——NULL 本就不该算作一种商品)
PostgreSQL 和 SQL Server 的兼容细节
语法上三者都支持 COUNT(DISTINCT column),但行为微差:
- PostgreSQL 对
TEXT字段去重完全正常;SQL Server 要求product_id是可比较类型(比如不能是XML或IMAGE) - SQL Server 2016+ 支持,但老版本(如2008R2)不支持
DISTINCT在聚合中,得用子查询模拟:HAVING COUNT(*) = (SELECT COUNT(DISTINCT product_id) FROM orders o2 WHERE o2.user_id = orders.user_id) - 如果
product_id是字符串且含前后空格,'abc '和'abc'被视为不同——建议清洗数据或用TRIM()包裹
NULL 干扰和旧数据库版本限制,尤其是交接别人留下的 SQL Server 2008 环境,一跑就报错。










