SQL如何快速找出重复数据并编号 ROW_NUMBER去重技巧
SQL如何快速找出重复数据并编号 ROW_NUMBER去重技巧

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
用 ROW_NUMBER() 给重复行打序号,再筛出重复组
开门见山,先说一个核心结论:ROW_NUMBER() 这个窗口函数本身并不具备“去重”的魔法,它的核心任务仅仅是编号。真正的去重操作,需要你配合子查询或者公共表表达式(CTE),把那些编号为1(或者大于1)的行精准地筛选出来。整个过程的关键,首先在于明确定义“究竟依据哪些字段来判断重复”,然后通过 PARTITION BY 子句来分组编号。
一个新手常踩的坑,是写成 ROW_NUMBER() OVER (ORDER BY col) 这种形式。这会导致在全表范围内进行连续编号,根本没有进行分组,自然也就无法识别出哪些记录是彼此的“复制品”。
- 定义分组是关键:必须使用
PARTITION BY col1, col2来指定判定重复的字段组合,比如单一的email字段,或者复合的user_id, order_date。 - 组内排序求稳定:括号内的
ORDER BY只负责控制每个分组内部的排序逻辑。通常选用id或created_at这类具有唯一性或时间顺序的字段,以确保每次执行的结果都稳定一致。 - 编号逻辑要看清:编号从1开始,且在每个分组内独立计数。因此,对于重复的数据组,必然会出现
rn = 1(通常被视作保留行)和rn > 1(通常被视作待处理行)的记录。
查出所有重复记录(含编号),方便人工核对
这一步至关重要,却最容易被跳过:还没看清楚重复数据到底长什么样,就急着动手删除,很容易导致误删。更稳妥的做法是,先用CTE把编号结果封装起来,同时结合 COUNT(*) OVER (PARTITION BY ...) 窗口函数,直接计算出每组的记录条数。这样,数据状况便一目了然。
WITH duped AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS rn,
COUNT(*) OVER (PARTITION BY email) AS cnt
FROM users
)
SELECT * FROM duped WHERE cnt > 1;
这里需要特别注意两个筛选条件的区别:cnt > 1 用于筛选出“所有属于重复组”的行;而 rn = 1 则用于在重复组中“保留第一条”。在编写删除逻辑时,可千万别把这两个条件混淆了。
保留每组第一条,删掉其余重复行(安全删除写法)
直接执行 DELETE 语句风险较高,务必先进行数据备份或在事务中测试。此处的核心难点在于,如何让数据库精准定位“哪些行该被删除”。通常的做法是通过子查询关联原表的主键,而不是试图直接用 ROW_NUMBER() 的生成列进行删除(部分数据库不支持这种语法)。
- 语法因数据库而异:PostgreSQL / SQL Server / Oracle 等数据库支持
DELETE ... USING或DELETE FROM ... WHERE id IN (SELECT ...)这类写法。 - MySQL的版本差异:MySQL 8.0+ 版本可以优雅地使用 CTE 配合
DELETE,但老版本可能需要借助临时表或自连接来实现。 - 一种更通用的替代方案:
DELETE FROM users WHERE id NOT IN ( SELECT MIN(id) FROM users GROUP BY email );
这种使用GROUP BY和MIN()聚合函数的方法比ROW_NUMBER()更简洁,但它有一个局限:你无法灵活控制“保留哪一条”,只能依赖MIN()或MAX()这类聚合规则。
ROW_NUMBER() 和 DENSE_RANK() 在去重场景的区别
是不是有人尝试过用 DENSE_RANK() 来代替 ROW_NUMBER() 进行去重,却发现编号结果“不对劲”?原因在于,去重逻辑依赖于“组内是否能为每行生成唯一的序号”,而 DENSE_RANK() 函数会对相同的排序值赋予相同的排名,这导致它无法有效区分同一分组内的不同行。
ROW_NUMBER():在组内生成严格的 1, 2, 3… 序列。这让我们可以精准定位“第一条”和“非第一条”记录,是去重场景的理想选择。RANK()或DENSE_RANK():当遇到相同的ORDER BY值时,会产生并列排名(例如 1, 1, 3 或 1, 1, 2)。这种情况下,你无法简单地用rn = 1来安全地保留唯一的一行。- 从性能角度看,这几个函数差异不大,但语义清晰度天差地别。所以,别为了省事而随意替换函数。
话说回来,真正棘手的往往不是SQL怎么写,而是“重复”这个定义本身是否合理。举个例子,忽略空格、大小写或时区偏移的邮箱地址,到底算不算重复?这些问题必须在 PARTITION BY 之前就处理好,比如使用 TRIM(LOWER(email)) 进行标准化。否则,后续的编号分组从一开始就错了,再精巧的语法也是徒劳。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
怎样检测SQL注入是否造成了数据泄露_分析数据库审计日志与异常流量
如何准确判断SQL注入是否导致数据泄露?仅靠SELECT日志远远不够 一个核心的检测误区是:仅仅在数据库审计日志中搜索SELECT或UNION SELECT关键词,并不能直接证明数据已经发生泄露。攻击是否成功,真相往往隐藏在语句执行结果、用户权限上下文以及敏感数据访问行为这三者的交叉分析与关联验证之
SQL如何实现多表JOIN后的批量删除逻辑_对比不同DB语法差异
SQL如何实现多表JOIN后的批量删除逻辑:对比不同DB语法差异 想用一条SQL语句,基于多表关联的结果来批量删除数据?这事儿听起来简单,但不同数据库的语法差异,足以让开发者踩坑。核心的挑战在于:如何精准定位要删除的行,同时避免误删和性能陷阱。先明确一个关键点: MySQL支持DELETE JOIN
SQL查询如何计算分组后的加权平均数_SUM乘积除以SUM权重
SQL查询如何计算分组后的加权平均数:SUM乘积除以SUM权重 说到加权平均,一个常见的误区是直接使用 A VG() 函数。但仔细想想,A VG() 默认对所有值一视同仁,这显然不符合“权重”的本意。真正的加权平均,核心在于“权重必须参与分母计算”。所以,正确的公式是:SUM(value * wei
如何解决SQL语句中注释符(--)引起的注入_剥离输入字符串中的符号
如何解决SQL语句中注释符(--)引起的注入_剥离输入字符串中的符号 SQL注入中 -- 注释符为什么危险 问题的核心在于,数据库引擎会将 -- 之后的所有内容都视为注释而直接忽略。这就给了攻击者一个绝佳的“手术刀”,可以精准地截断原有的SQL逻辑,从而绕过身份验证或拼接上恶意指令。 举个典型的例子
如何在SQL存储过程中判断临时表是否存在_使用OBJECT_ID函数校验
SQL存储过程如何准确判断临时表是否存在?OBJECT_ID函数权威指南 在SQL Server存储过程开发中,准确判断临时表是否存在是确保脚本健壮性的关键一步。经过大量实践验证,使用 object_id( tempdb 表名 ) 是最可靠、最标准的解决方案,其他替代方法往往存在误判风险或兼容性
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

