SQL去重方法:轻松搞定重复数据

用DISTINCT去掉重复行

在日常处理数据时,经常会遇到一张表里有大量重复记录的情况。比如用户注册信息表中,同一个手机号被提交了两次。这时候最简单的办法就是用 DISTINCT 关键字。

假设我们有一张叫 users 的表,里面包含姓名和手机号,想查出所有不重复的手机号,可以这样写:

SELECT DISTINCT phone FROM users;

这条语句会自动过滤掉重复的手机号,只返回唯一的值。如果是多个字段组合去重,比如姓名和电话一起判断是否重复,也可以:

SELECT DISTINCT name, phone FROM users;

只要这两个字段的组合是相同的,就视为一条重复数据。

使用GROUP BY实现更灵活的去重

当需要结合聚合函数,比如统计每条记录出现的次数时,GROUP BY 就比 DISTINCT 更实用。还是上面的用户表,如果我们不仅想去重,还想看看哪些手机号重复了几次,可以用:

SELECT phone, COUNT(*) as count FROM users GROUP BY phone HAVING count > 1;

这里先按手机号分组,再通过 HAVING 筛选出出现次数大于1的号码,能快速定位到问题数据。

保留最新一条记录的去重方式

实际业务中,可能每个用户有多条操作日志,现在要按用户去重,但必须保留最后一条记录。这时候可以用窗口函数 ROW_NUMBER()

假设日志表叫 user_logs,有字段 user_idaction_timeaction,我们要按用户ID分区,并按时间倒序编号:

SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY action_time DESC) as rn
FROM user_logs
) t
WHERE rn = 1;

这个查询会给每个用户的每条记录打上序号,最新的那条是1,然后外部筛选出序号为1的记录,就实现了“每人只留最新一条”的去重要求。

删除表中完全重复的记录

有时候表里真的存在一模一样的多行数据,没有主键或唯一标识,这时候删除重复项可以借助临时表或者CTE(公用表表达式)。

例如,在MySQL 8.0+环境中,可以用如下方式:

WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY (SELECT NULL)) as rn
FROM your_table
)
DELETE FROM your_table
WHERE (col1, col2) IN (
SELECT col1, col2 FROM cte WHERE rn > 1
);

不过注意,不同数据库对DELETE结合子查询的支持程度不一样,有些可能需要先插入到临时表再重建原表。

利用主键或自增ID去重

如果表中有自增ID,即使其他字段都一样,也可以通过最大ID来保留一条记录。比如我们倾向于保留后插入的数据:

DELETE u1 FROM users u1
INNER JOIN users u2
WHERE u1.id < u2.id
AND u1.phone = u2.phone;

这条语句适用于MySQL,它会把相同手机号中ID较小的记录删掉,留下ID大的那条,也就是后来添加的。