在MySQL的实际使用中,表中出现重复行是比较常见的问题,可能由数据导入重复、业务逻辑漏洞等原因导致。针对不同场景的重复行,MySQL提供了多种消除方法,下面逐一分析。

使用DISTINCT关键字消除重复行
DISTINCT是最简单的消除重复行的方式,它会返回查询结果中唯一不重复的记录,适用于只需要查询去重后结果的场景,不会对原表数据做修改。
基本语法
SELECT DISTINCT 列名1, 列名2,... FROM 表名 [WHERE 条件];
代码示例
假设有一张用户表user,结构如下:
-- 创建用户表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT,
city VARCHAR(50)
);
-- 插入测试数据,包含重复记录
INSERT INTO user (name, age, city) VALUES
('张三', 20, '北京'),
('李四', 25, '上海'),
('张三', 20, '北京'),
('王五', 30, '广州'),
('李四', 25, '上海');
如果要查询所有不重复的用户姓名和城市组合,可以使用以下语句:
SELECT DISTINCT name, city FROM user;
执行后会返回三条记录,重复的张三北京、李四上海组合只保留一条。
使用GROUP BY子句消除重复行
GROUP BY除了用于分组统计,也可以用来消除重复行,它可以将相同值的行分为一组,每组只返回一条记录,还可以配合聚合函数做统计操作。
基本语法
SELECT 列名1, 列名2,... FROM 表名 [WHERE 条件] GROUP BY 列名1, 列名2,...;
代码示例
同样使用上面的user表,要获取不重复的姓名和城市组合,也可以这样写:
SELECT name, city FROM user GROUP BY name, city;
如果需要统计每个不重复的用户组合出现的次数,可以配合COUNT函数:
SELECT name, city, COUNT(*) AS repeat_count FROM user GROUP BY name, city;
执行后会返回每个组合的出现次数,方便排查重复数据的规模。
使用子查询和ROW_NUMBER()窗口函数消除重复行
如果需要删除表中的重复行,只保留一条,上面的方法只能查询去重结果,无法修改原表。这时候可以使用ROW_NUMBER()窗口函数给重复行编号,再删除编号大于1的行。
基本逻辑
先按重复判断的列分区,给每个分区的行按规则编号,然后删除编号不是1的行,就可以保留每组重复数据中的一条。
代码示例
要删除user表中name和city都重复的记录,只保留id最小的那条,可以执行以下语句:
-- 先给重复行编号
WITH temp AS (
SELECT id, ROW_NUMBER() OVER (PARTITION BY name, city ORDER BY id ASC) AS rn
FROM user
)
-- 删除编号大于1的重复行
DELETE FROM user WHERE id IN (SELECT id FROM temp WHERE rn > 1);
执行后,表中重复的张三北京、李四上海记录都会被删除,只保留id最小的那条。
不同方法对比
以下是几种消除重复行方法的特点对比:
| 方法 | 适用场景 | 是否会修改原表 | 优点 | 缺点 |
|---|---|---|---|---|
| DISTINCT | 查询时去重,不需要修改原表 | 否 | 语法简单,执行效率高 | 只能用于查询,无法处理原表重复数据 |
| GROUP BY | 查询去重同时需要分组统计 | 否 | 可以配合聚合函数做统计 | 只能用于查询,无法处理原表重复数据 |
| ROW_NUMBER()子查询 | 需要删除原表重复行,保留指定一条 | 是 | 可以精准控制保留哪条重复记录 | 语法相对复杂,需要MySQL 8.0及以上版本支持窗口函数 |
注意事项
- 使用DISTINCT时,如果查询多列,只有所有列的值都相同时才会判定为重复行。
- 使用GROUP BY时,SELECT后面的列要么是分组列,要么是被聚合函数处理的列,否则可能返回不确定的结果。
- 删除重复行之前建议先备份数据,避免误操作导致数据丢失。
- 如果MySQL版本低于8.0,无法使用ROW_NUMBER()窗口函数,可以用自连接的方式实现删除重复行的需求。