mysql如何使用distinct去重_mysql去重操作解析

1次阅读

distinct仅作用于select列表,修饰整行而非单列;不能用于where或order by;NULL被视为相同值;复杂去重需用group by或row_number()。

mysql如何使用distinct去重_mysql去重操作解析

distinct 只能作用于 select 列表,不能用于 where 或 order by

很多人误以为 DISTINCT 是个独立指令,可以像函数一样“对某列去重”,其实它修饰的是整个 SELECT 行。只要两行在 SELECT 中列出的所有字段组合完全一致,就只保留一行。

常见错误写法:SELECT DISTINCT name, age FROM user WHERE DISTINCT city = 'Beijing' —— 这会直接报错,因为 DISTINCT 不是关键字,不能出现在 WHERE 子句里。

  • DISTINCT 必须紧跟在 SELECT 后面,且只能出现一次
  • 如果选了 SELECT DISTINCT name, age,那么 (张三, 25)(张三, 26) 算两行,不会被去重
  • 想按单列逻辑去重但又想取其他字段?不能靠 DISTINCT 直接实现,得用 GROUP BY 或窗口函数

distinct 和 group by 在语义和性能上并不等价

DISTINCT 看似简单,但 mysql 内部常把它重写为隐式 GROUP BY(尤其在有聚合函数时),而显式 GROUP BY 允许你控制聚合逻辑、使用 ANY_VALUE()MIN()/MAX() 选取代表值。

例如:要取每个 city 下任意一条用户记录(含 id、name、age):

SELECT DISTINCT city, name, age FROM user;

这并不能保证“每个 city 只返回一条”,因为 city 相同但 nameage 不同时仍会多行;真正想要的其实是:

SELECT city, ANY_VALUE(name), ANY_VALUE(age) FROM user GROUP BY city;
  • MySQL 5.7+ 开启 ONLY_FULL_GROUP_BY 时,SELECT city, name FROM user GROUP BY city 会报错,必须对非分组列用聚合函数包裹
  • ANY_VALUE() 是最轻量的“选一个就行”的方式,不保证稳定性,但性能好
  • DISTINCT 无法表达“每组取最新一条”这种需求,必须配合子查询或 ROW_NUMBER()(MySQL 8.0+)

distinct 对 null 值的处理:多个 null 被视为相同

这是容易被忽略的细节:NULLDISTINCT 中被当作相等值处理。比如:

SELECT DISTINCT status FROM orders;

status'paid''shipped'NULLNULL,结果只有三行 —— 所有 NULL 合并为一个。

  • 这点和 GROUP BY 一致,但和 ORDER BY 不同(ORDER BY col ASCNULL 默认排最前)
  • 如果业务上需要区分“未填”和“明确为空”,建议用字符串'unknown' 替代 NULL,避免去重逻辑意外吞掉数据
  • 联合去重时,(1, NULL)(1, NULL) 被去重,但 (1, NULL)(1, 'a') 不会被去重

替代 distinct 的更可控方案:row_number() + 子查询(MySQL 8.0+)

当你要“每个分组取最新/最早/指定排序下的一条”,DISTINCT 完全无能为力,必须换思路。

例如:每个 user_id 取最新一条订单(按 created_at 降序):

SELECT user_id, order_no, created_at FROM (   SELECT user_id, order_no, created_at,          ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn   FROM orders ) t WHERE rn = 1;
  • 这个模式比 GROUP BY + MAX(created_at) 再关联原表更简洁,也避免了关联丢失字段的风险
  • 注意 ROW_NUMBER() 是窗口函数,必须在子查询或 CTE 中使用,不能直接在外部 WHERE 里引用别名 rn
  • 如果用的是 MySQL 5.7 或更低版本,只能用自连接或相关子查询,性能差很多,建议升级或加索引优化

实际去重操作中最容易卡住的地方,不是语法写不对,而是没想清楚“到底要按什么维度去重”以及“其他字段怎么选”。DISTINCT 只解决“整行唯一”这一种情况,稍复杂一点的业务场景,就得切换到 GROUP BY 或窗口函数。

text=ZqhQzanResources