SELECT DISTINCT 作用于整行而非单字段,NULL被视为相同值且只保留一个,大数据量下性能差需优化,ORDER BY字段必须出现在SELECT列表中。

SELECT DISTINCT 作用的是整个 SELECT 列表,不是某一个字段
很多人写 SELECT DISTINCT name, class FROM students,本意是“只按 name 去重”,结果却得到一堆 name 相同但 class 不同的行。这是因为 DISTINCT 判断重复的单位是整行——即 (name, class) 这个组合。只要组合值不同,就算不同行。
常见错误现象:
- 查“不同用户”却写了
SELECT DISTINCT user_id, city FROM orders,导致同一用户在多个城市下单时被计为多条 - 误以为
DISTINCT会“优先保留第一条”,其实它不保证顺序,也不保留关联字段的任意合理值
正确做法:
- 真要只按一列去重 → 写
SELECT DISTINCT user_id FROM orders - 想按一列去重但还要带出其他字段(比如最新订单时间)→ 不能靠
DISTINCT,得用GROUP BY或窗口函数
NULL 值会被视为相同值,但只保留一个
MySQL 把所有 NULL 当作相等来处理。所以 SELECT DISTINCT city FROM users 中,哪怕表里有 10 行 city IS NULL,结果里也只出现一次 NULL。
这和 WHERE city = city 或 GROUP BY city 的行为一致,但容易被忽略:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 如果你在统计“非空城市数”,
COUNT(DISTINCT city)会把NULL算进去,需显式排除:COUNT(DISTINCT CASE WHEN city IS NOT NULL THEN city END) - 导出数据时发现少了几条,可能就是因为空值被合并了,建议先用
SELECT COUNT(*), COUNT(city), COUNT(DISTINCT city) FROM users对比验证
大数据量下 DISTINCT 性能差,别直接全表扫
DISTINCT 底层依赖排序或哈希去重,当查询字段没索引、又没加 WHERE 时,MySQL 往往要扫描全表并构建临时内存结构。百万级表查 SELECT DISTINCT text_column FROM huge_table 可能卡住或触发磁盘临时表。
优化路径很实际:
- 先缩小数据范围:
SELECT DISTINCT city FROM users WHERE created_at > '2025-01-01' - 给常用去重字段建索引;注意
TEXT类型只能建前缀索引,比如ALTER TABLE users ADD INDEX idx_city (city(50)) - 如果只是要数量,用
COUNT(DISTINCT city)比先查再去重更省资源 - 避免在
DISTINCT后选大字段(如长文本、JSON),它们显著拖慢哈希计算
ORDER BY 字段必须出现在 SELECT 列表中
这条语句会报错:SELECT DISTINCT city FROM users ORDER BY score DESC,因为 score 不在 SELECT 里,MySQL 无法对未选中的列排序。
这不是 MySQL 的 bug,而是 SQL 标准要求:排序依据必须是结果集里的列(或其表达式)。解决方式只有两个:
- 把排序字段加进
SELECT和DISTINCT,比如SELECT DISTINCT city, score FROM users ORDER BY score DESC—— 但这时去重粒度变成(city, score),语义已变 - 改用
GROUP BY:SELECT city FROM users GROUP BY city ORDER BY MAX(score) DESC,这才是“按城市分组,再按该城市最高分排序”的真实意图
真正难缠的点在于:你写的 DISTINCT 语句看似能跑通,但只要涉及排序、聚合或关联其他表,就很容易滑向语义模糊区——这时候它已经不是“去重工具”,而是在掩盖逻辑缺陷。

















