mysql 去重(长文解析)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在数据库开发中,"mysql 去重"是一个高频需求。想象你正在管理一个图书馆的借阅记录表,当发现多本同名书籍被重复登记时,如何快速筛选出唯一名称的书籍?类似地,开发者常需要从数据库中删除冗余数据或统计唯一值。本文将通过DISTINCTGROUP BY等核心工具,结合实际案例,逐步拆解 MySQL 去重的实现方法与优化策略。


基础语法:DISTINCT 的核心用法

什么是去重?

"去重"即从数据集合中筛选出唯一值。在 MySQL 中,最基础的去重工具是 DISTINCT 关键字。它像一个"过滤器",能自动忽略重复的字段值。

示例:查询唯一用户名称

假设有一个用户表 users,包含以下字段:

CREATE TABLE users (  
    id INT PRIMARY KEY,  
    name VARCHAR(50),  
    email VARCHAR(100)  
);  

插入测试数据:

INSERT INTO users (id, name, email)  
VALUES  
(1, 'Alice', 'alice@example.com'),  
(2, 'Bob', 'bob@example.com'),  
(3, 'Alice', 'alice2@example.com'),  
(4, 'Charlie', 'charlie@example.com');  

若想查询所有不同名称的用户,使用以下语句:

SELECT DISTINCT name FROM users;  

执行结果

+--------+  
| name   |  
+--------+  
| Alice  |  
| Bob    |  
| Charlie|  
+--------+  

DISTINCT 的局限性

  • 只能针对单个字段去重。若需多字段组合去重(例如同时筛选唯一 nameemail),需将字段写入同一 DISTINCT 子句中:
    SELECT DISTINCT name, email FROM users;  
    
  • 无法直接删除重复数据,仅能返回去重后的结果集。

进阶技巧:GROUP BY 与多条件去重

当需要统计重复数据或处理多字段去重时,GROUP BY 是更灵活的工具。

案例:统计重复名称的用户数量

SELECT name, COUNT(*) AS count  
FROM users  
GROUP BY name  
HAVING count > 1;  

执行结果

+--------+-------+  
| name   | count |  
+--------+-------+  
| Alice  |     2 |  
+--------+-------+  

GROUP BY 的优势

  • 可结合聚合函数(如 COUNTSUM)实现复杂统计。
  • 通过 HAVING 筛选满足条件的分组,例如找出重复超过三次的记录。

子查询与临时表:处理复杂去重场景

当需要删除重复数据时,DISTINCTGROUP BY 无法直接操作。此时可通过子查询或临时表定位重复项。

案例:删除重复的用户记录

假设 users 表中存在完全重复的 nameemail 记录:

INSERT INTO users (id, name, email)  
VALUES (5, 'Bob', 'bob@example.com');  

删除重复数据的步骤

  1. 通过 GROUP BY 找到重复的组合:
    SELECT name, email, MIN(id) AS keep_id  
    FROM users  
    GROUP BY name, email  
    HAVING COUNT(*) > 1;  
    

    结果

    +------+---------------------+----------+  
    | name | email               | keep_id  |  
    +------+---------------------+----------+  
    | Bob  | bob@example.com     |        2 |  
    +------+---------------------+----------+  
    
  2. 删除非最小 id 的重复记录:
    DELETE FROM users  
    WHERE (name, email) IN (  
        SELECT name, email FROM users GROUP BY name, email HAVING COUNT(*) > 1  
    )  
    AND id NOT IN (  
        SELECT MIN(id) FROM users GROUP BY name, email HAVING COUNT(*) > 1  
    );  
    

窗口函数:高效去重的现代方案

MySQL 8.0 引入的窗口函数(如 ROW_NUMBER())为去重提供了更简洁的方法。

案例:标记重复行并删除

WITH ranked_users AS (  
    SELECT  
        id,  
        name,  
        email,  
        ROW_NUMBER() OVER (PARTITION BY name, email ORDER BY id) AS rn  
    FROM users  
)  
DELETE FROM users  
WHERE id IN (SELECT id FROM ranked_users WHERE rn > 1);  

解释

  • ROW_NUMBER() 为每组重复记录分配序号。
  • 删除序号大于 1 的记录,保留第一条。

性能优化:让去重查询更高效

1. 索引的作用

在去重字段上添加索引可显著提升查询速度。例如:

ALTER TABLE users ADD INDEX idx_name_email (name, email);  

2. 避免使用 SELECT *

去重时仅选择必要字段,减少数据传输量。

-- 低效写法  
SELECT * FROM users GROUP BY name;  

-- 高效写法  
SELECT name FROM users GROUP BY name;  

3. 使用 EXPLAIN 分析执行计划

通过 EXPLAIN 检查查询是否使用索引:

EXPLAIN SELECT DISTINCT name FROM users;  

理想情况下,type 列应显示 index,表示直接使用索引扫描。


常见问题与解决方案

问题 1:去重后如何保留其他字段?

若需保留非去重字段(如 id),可结合 GROUP BY 和聚合函数:

SELECT  
    name,  
    MIN(id) AS first_id,  
    MAX(email) AS latest_email  
FROM users  
GROUP BY name;  

问题 2:如何处理部分字段重复?

假设需保留 name 唯一,但允许 email 不同:

SELECT * FROM users  
WHERE name IN (  
    SELECT name FROM users GROUP BY name HAVING COUNT(*) = 1  
);  

结论

"mysql 去重"是开发者必须掌握的核心技能。从基础的 DISTINCT 到进阶的窗口函数,每种方法都有其适用场景。通过合理使用索引、避免全表扫描,甚至结合事务确保数据一致性,开发者可以高效处理去重需求。建议读者在实际项目中结合案例练习,并通过 EXPLAIN 持续优化查询性能。掌握这些技巧后,你将能从容应对数据冗余带来的挑战。

最新发布