MySQL 5.7及更早版本不支持ROW_NUMBER(),执行会报错FUNCTION xxx.ROW_NUMBER does not exist;必须通过SELECT VERSION()确认版本≥8.0.2才能使用,否则需改用自连接或临时表方案。

确认 MySQL 版本是否支持 ROW_NUMBER()
ROW_NUMBER() 在 MySQL 5.7 及更早版本中完全不可用,强行执行会报错 FUNCTION xxx.ROW_NUMBER does not exist。必须先运行:SELECT VERSION();
确保输出为 8.0.2 或更高(如 8.0.33)。低于该版本请改用自连接或临时表方案,别硬套窗口函数。
用 PARTITION BY + ORDER BY 构建去重逻辑
PARTITION BY 指定“按哪些字段判断重复”,ORDER BY 决定“哪条留下”。漏写 PARTITION BY 会导致整表编号,结果只剩第一条——这不是去重,是截断。
常见错误包括:
• 把 ORDER BY id 当成“保留最早插入”,但 id 不连续或被跳过时不可靠
• 多个排序字段未加确定性兜底,比如 ORDER BY created_at DESC 遇到秒级时间重复,结果每次执行不一致
• 字段含大量 NULL,MySQL 8.0+ 默认把 NULL 排在最后,若想统一排前面,得写 ORDER BY created_at IS NULL, created_at DESC
DELETE 语句必须绕过 ERROR 1093
MySQL 禁止在 DELETE 中直接嵌套对同一张表的子查询(含窗口函数),否则报错 ERROR 1093: You can't specify target table ... for update in FROM clause。正确做法只有两种:
• 用 CTE 包一层再删:
WITH ranked AS (<br> SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY updated_at DESC, id DESC) AS rn<br> FROM users<br>)<br>DELETE FROM users WHERE id IN (SELECT id FROM ranked WHERE rn > 1);
• 用派生表(兼容性更好,MySQL 5.7 也能跑语法):
DELETE t1 FROM users t1 INNER JOIN (SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY updated_at DESC) AS rn FROM users) t2 ON t1.id = t2.id WHERE t2.rn > 1;注意:两个方案都必须显式写出主键(如
id),不能写 *,否则 IN 或 JOIN 会失败。
线上执行前务必验证和备份
去重操作不可逆,尤其在大表上容易误删。执行前必须做三件事:
• 先查将删多少行:SELECT COUNT(*) FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY email ORDER BY updated_at DESC) AS rn FROM users) t WHERE rn > 1;
• 备份原表:CREATE TABLE users_backup AS SELECT * FROM users;
• 在从库或测试环境跑通全流程,特别是 ORDER BY 字段有 NULL 或重复值时,rn = 1 是否真对应业务上要留的那条——这取决于排序字段的真实分布,不是语法写对就万事大吉。


















