DISTINCT 作用于整行所有字段的组合值,而非单列;它不处理业务逻辑去重,无法识别“最新”“最高”等语义,仅机械比较值是否完全相同。

SELECT DISTINCT 作用的是整行,不是某列
DISTINCT 不是“按第一列去重”,而是对 SELECT 列表中**所有字段的组合值**做唯一性判断。只要任意一列值不同,整行就被视为不重复。
常见错误现象:查 users 和 orders 表时写 SELECT DISTINCT u.name, o.order_id,结果每条订单都出来——因为每个 order_id 都唯一,哪怕 u.name 完全一样。
- 检查你是否无意中带入了主键、时间戳、自增ID等天然唯一字段
- 用
DESCRIBE或SHOW COLUMNS确认各字段类型,避免字符串'123'和整数123被当成不同值 - NULL 值不会被合并:两行都是
name = 'Alice'且phone = NULL,DISTINCT 仍会返回两行
多表 JOIN 后直接套 DISTINCT 是低效且易错的
嵌套查询里把 DISTINCT 放在外层 SELECT,但内层 JOIN 已经因一对多关系产生笛卡尔积,这时外层去重只是“擦屁股”,既拖慢查询,又掩盖逻辑缺陷。
比如 users JOIN orders ON u.id = o.user_id,一个用户有 5 条订单,JOIN 后就生成 5 行;DISTINCT u.id, u.name 看似能压成 1 行,但实际可能因其他字段(如 o.created_at)不同而失败。
- 先确认 JOIN 是否真需要——有时该用
EXISTS或IN替代 - 若必须保留订单信息,改用
GROUP BY u.id, u.name+ 聚合函数(如MAX(o.created_at))更可控 - MySQL 5.7+ 严格模式下,
SELECT u.id, u.name, o.order_id GROUP BY u.id会报错,必须把所有非聚合列放进GROUP BY
想“按某列逻辑去重”,DISTINCT 本身就不适用
当你真正想要的是“每个用户只取最新一条订单”或“每个城市只取人口最多的一条记录”,DISTINCT 没有排序、没有优先级、不支持窗口函数,它只认“值相等”。这种场景它根本不是解法。
例如:SELECT DISTINCT city, population FROM cities 不会自动挑出每个 city 下最大的 population,只会筛掉完全相同的 (city, population) 组合。
- 替代方案优先级:窗口函数(
ROW_NUMBER() OVER (PARTITION BY city ORDER BY population DESC)) > 子查询 +GROUP BY>LEFT JOIN自关联 - 如果数据库版本太老不支持窗口函数,用子查询先取关键 ID 再 JOIN 回原表,比硬套
DISTINCT更可靠 -
GROUP BY虽可替代部分需求,但它强制要求聚合逻辑清晰,不能裸露非分组列
NULL 和隐式类型转换会让 DISTINCT “看起来失效”
两个 NULL 在 DISTINCT 中不相等,这是 SQL 标准行为;同样,CAST('1' AS CHAR) 和 CAST(1 AS SIGNED) 在多数 MySQL 排序规则下也不被视为相同。
典型表现:查 SELECT DISTINCT phone FROM users,明明看到多行都是空,却返回好几条 NULL;或者导出数据时发现 '001' 和 1 并存。
- 统一空值:用
COALESCE(phone, '')或IFNULL(phone, 'N/A')替换NULL - 强制类型一致:对数字型字段加
CAST(col AS CHAR),对字符串字段用TRIM()清空格 - 注意 collation:
utf8mb4_0900_as_cs(大小写敏感)和utf8mb4_general_ci对 'A' 和 'a' 的判定不同
DISTINCT,而是意识到什么时候不该用它——尤其在涉及业务语义的“去重”时,它只是一个机械的逐行比较工具,不理解“最新”“最高”“首次”这些概念。


















