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