row_number()仅按规则编号,识别重复需结合partition by字段与“rn>1”判断;partition by必须严格匹配业务重复定义,order by决定组内优先级,编号为1者为每组代表。

用 ROW_NUMBER() 标记重复记录的最简逻辑
直接结论:ROW_NUMBER() 本身不判断重复,它只按规则给行编号;真正识别重复,得靠“对相同字段分组后编号 > 1”的组合逻辑。核心不是函数本身,而是 PARTITION BY 的字段是否覆盖你定义的“重复标准”。
比如你认为 user_id 和 order_date 完全相同时算重复,那 PARTITION BY user_id, order_date 就是必须项;若只按 user_id 分组,那同用户所有记录都会被编号,哪怕日期不同——这不是你想要的重复。
-
PARTITION BY字段必须严格对应业务中“视为同一重复组”的条件 -
ORDER BY在组内决定哪条算“第一”,通常用时间戳或主键(如created_at DESC保最新,id ASC保最早) - 编号为
1的行是每组的“代表”,其余都是重复候选——但是否真删/标为重复,还得结合业务规则再过滤
写法示例:标记重复并保留最新一条
常见需求:查出所有重复记录,但把每组里 updated_at 最大的那条标为非重复(即保留),其余标为重复。
SELECT
id,
user_id,
order_date,
updated_at,
CASE WHEN rn = 1 THEN 'keep' ELSE 'duplicate' END AS status
FROM (
SELECT
id,
user_id,
order_date,
updated_at,
ROW_NUMBER() OVER (
PARTITION BY user_id, order_date
ORDER BY updated_at DESC, id DESC
) AS rn
FROM orders
) t;
注意这里 ORDER BY updated_at DESC, id DESC 是防时间相同时排序不确定;如果业务允许任意保留一条,单写 ORDER BY updated_at DESC 即可。
ROW_NUMBER() vs COUNT(*) OVER:选哪个更合适?
如果只需“知道某行是否属于重复组”,COUNT(*) OVER (PARTITION BY ...) 更直观:结果直接是组内总行数,> 1 即表示重复。
- 用
ROW_NUMBER():适合需要排序、取 Top N、或明确区分“首条/非首条”的场景 - 用
COUNT(*) OVER:适合纯判断“是否重复”、且不关心组内顺序时,语义更直白,性能在大数据量下略优(少一次排序) - 二者都不能替代
GROUP BY + HAVING做聚合统计,那是另一类需求
例如只标状态,不用排序逻辑:CASE WHEN COUNT(*) OVER (PARTITION BY user_id, order_date) > 1 THEN 'duplicate' ELSE 'unique' END 更轻量。
容易踩的坑:NULL 值和数据类型隐式转换
PARTITION BY 中字段含 NULL 时,所有 NULL 会被归为同一组——这常导致误判重复。比如 phone 为空的多条记录,会被当成“相同手机号”而标为重复。
- 显式处理
NULL:PARTITION BY COALESCE(phone, CONCAT('null_', id))或用CASE WHEN phone IS NULL THEN -1 ELSE phone END - 字符串字段注意前后空格:
TRIM(phone)再参与分组 - 数字型 ID 和字符串型 ID 混用会触发隐式转换,导致分组错乱,务必统一类型(如都转
CAST(id AS VARCHAR))
这些细节不报错,但结果偏差隐蔽,上线前一定要用含 NULL 和空值的真实样本验证。










