mysql查询重复数据,日常数据清洗和校验的常见需求

时间:2025-09-15 15:38

在 MySQL 中查询重复数据是日常数据清洗和校验的常见需求,核心思路是通过 分组(GROUP BY 统计字段重复次数,再用 过滤条件(HAVING 筛选出重复记录。以下分场景详细介绍查询方法,覆盖 “单字段重复”“多字段组合重复”“查询完整重复行” 等核心需求,并提供去重思路。

一、核心原理

重复数据的判定依赖 “重复维度”(单字段 / 多字段),关键语法组合:

  • GROUP BY 重复字段:按目标字段分组,相同值的记录会被归为一组;
  • COUNT(*) >= 2:统计每组的记录数,大于等于 2 表示存在重复;
  • HAVING:过滤分组后的结果(区别于 WHERE 过滤行数据,HAVING 仅作用于分组)。

二、分场景查询重复数据

1. 场景 1:单字段重复(最常见)

需求:查询某一列中存在重复的值(如 “用户表中重复的手机号”“订单表中重复的订单号”)。
1.1 仅查询 “重复的字段值” 及重复次数
适用于快速定位 “哪些值重复了”“重复了多少次”。

语法

sql
SELECT 
  重复字段名, 
  COUNT(*) AS 重复次数  -- 统计每组重复次数
FROM 
  表名
GROUP BY 
  重复字段名  -- 按目标字段分组
HAVING 
  COUNT(*) >= 2;  -- 筛选出重复(次数≥2)的组
 

示例:查询 user 表中重复的 phone(手机号)及重复次数:

sql
SELECT 
  phone, 
  COUNT(*) AS 重复次数
FROM 
  user
GROUP BY 
  phone
HAVING 
  COUNT(*) >= 2;
 

结果示例

phone 重复次数
13800138000 3
13900139000 2
1.2 查询 “完整的重复记录”(含所有字段)
适用于需要查看 “重复记录的全部信息”(如重复手机号对应的用户 ID、姓名),需用 子查询 + 关联 实现(先定位重复字段值,再关联原表查完整行)。

语法

sql
SELECT 
  *  -- 查询所有字段(或指定需查看的字段)
FROM 
  表名
WHERE 
  重复字段名 IN (
    -- 子查询:先找出所有重复的字段值
    SELECT 重复字段名
    FROM 表名
    GROUP BY 重复字段名
    HAVING COUNT(*) >= 2
  )
ORDER BY 
  重复字段名;  -- 按重复字段排序,便于对比
 

示例:查询 user 表中所有手机号重复的完整用户记录:

sql
SELECT 
  *
FROM 
  user
WHERE 
  phone IN (
    SELECT phone
    FROM user
    GROUP BY phone
    HAVING COUNT(*) >= 2
  )
ORDER BY 
  phone;
 

结果示例

id name phone create_time
1 张三 13800138000 2024-01-01 10:00:00
5 李四 13800138000 2024-01-03 14:30:00
8 王五 13800138000 2024-01-05 09:15:00
3 赵六 13900139000 2024-01-02 11:20:00
7 孙七 13900139000 2024-01-04 16:40:00

2. 场景 2:多字段组合重复

需求:判定 “多个字段同时相同” 为重复(如 “订单表中同一用户在同一时间创建的订单”“学生表中同一班级、同一姓名的学生”)。

核心:GROUP BY 后接 多个字段,仅当所有字段值均相同时才会被归为一组。
2.1 查询 “重复的字段组合” 及重复次数
语法

sql
SELECT 
  字段1, 
  字段2,  -- 组合重复的多个字段
  COUNT(*) AS 重复次数
FROM 
  表名
GROUP BY 
  字段1, 字段2  -- 按多个字段分组
HAVING 
  COUNT(*) >= 2;
 

示例:查询 order 表中 “同一用户(user_id)在同一时间(create_time)创建的重复订单”:

sql
SELECT 
  user_id, 
  create_time, 
  COUNT(*) AS 重复次数
FROM 
  `order`  -- order是关键字,需用反引号包裹
GROUP BY 
  user_id, create_time
HAVING 
  COUNT(*) >= 2;
 
2.2 查询 “完整的多字段重复记录”
语法

sql
SELECT 
  *
FROM 
  表名
WHERE 
  (字段1, 字段2) IN (  -- 用括号包裹字段组合
    SELECT 字段1, 字段2
    FROM 表名
    GROUP BY 字段1, 字段2
    HAVING COUNT(*) >= 2
  )
ORDER BY 
  字段1, 字段2;  -- 按组合字段排序
 

示例:查询 order 表中多字段重复的完整订单记录:

sql
SELECT 
  *
FROM 
  `order`
WHERE 
  (user_id, create_time) IN (
    SELECT user_id, create_time
    FROM `order`
    GROUP BY user_id, create_time
    HAVING COUNT(*) >= 2
  )
ORDER BY 
  user_id, create_time;
 

3. 场景 3:查询 “除主键外完全重复” 的记录

需求:表中存在主键(唯一标识,如 id),但其他字段完全相同(即 “仅主键不同,其余字段均相同” 的重复行)。

语法

sql
SELECT 
  *
FROM 
  表名 t1
WHERE 
  EXISTS (  -- 存在另一行,主键不同但其他字段相同
    SELECT 1
    FROM 表名 t2
    WHERE 
      t1.主键字段 != t2.主键字段  -- 排除同一行
      AND t1.字段1 = t2.字段1    -- 其他字段全部相等
      AND t1.字段2 = t2.字段2
      -- 若字段多,可简写为:t1.* <=> t2.*(<=> 包含NULL值对比)
  )
ORDER BY 
  字段1, 字段2;
 

示例:查询 user 表中 “仅 id 不同,其余字段均相同” 的重复记录:

sql
SELECT 
  *
FROM 
  user t1
WHERE 
  EXISTS (
    SELECT 1
    FROM user t2
    WHERE 
      t1.id != t2.id  -- 主键不同
      AND t1.name = t2.name
      AND t1.phone = t2.phone
      AND t1.create_time = t2.create_time
    -- 简写:AND t1.* <=> t2.*(适合字段多的场景,NULL值也会对比)
  )
ORDER BY 
  name, phone;
 

三、重复数据的去重思路(延伸)

查询到重复数据后,通常需要保留一条、删除其余重复行,核心是通过 主键 / 唯一标识 区分保留行和删除行:

示例:删除单字段重复的记录(保留 id 最小的行)

sql
DELETE FROM 
  user
WHERE 
  phone IN (
    SELECT phone FROM (
      -- 子查询嵌套:避免“不能直接删除FROM子句中引用的表”的错误
      SELECT phone
      FROM user
      GROUP BY phone
      HAVING COUNT(*) >= 2
    ) AS temp  -- 必须给子查询起别名
  )
  AND id NOT IN (
    -- 保留每组中id最小的行
    SELECT MIN(id) FROM (
      SELECT MIN(id) AS id
      FROM user
      GROUP BY phone
      HAVING COUNT(*) >= 2
    ) AS temp2
  );
 

说明:MySQL 不允许直接在 DELETE 的 WHERE 中引用当前删除的表,因此需要用 子查询嵌套(加别名) 规避该限制。

四、注意事项

  1. 性能问题:查询大表时,GROUP BY 可能消耗较多资源,建议:
    • 对 GROUP BY 的字段创建索引(如单字段重复时建 phone 索引,多字段重复时建 (user_id, create_time) 联合索引);
    • 避免用 SELECT *,仅查询需要的字段。
  2. NULL 值处理GROUP BY 中,NULL 会被视为相同值(即多个 NULL 会被归为一组);若需排除 NULL,可在 WHERE 中加 字段名 IS NOT NULL
  3. 关键字表名 / 字段名:若表名(如 order)或字段名(如 desc)是 MySQL 关键字,需用 反引号(`) 包裹,避免语法错误。
mysql删除重复数据只保留一条,选择适合你表结构的方法
闪客派对!FlashFXP 3.0 快乐通关秘籍
打造高信息密度WordPress主题:复刻知乎问答体验
Oracle数据库exp备份:幽默指南与教程
备份软件,让数据管理变得轻松愉快
逆袭学习法:如何启动你的垫底辣妹FTP
键盘一摔,老子要把“最近使用的文件”全扔进黑洞!
WordPress图片本地化存储策略
MySQL迁移至MSSQL的实践指南
北京石油化学院备份机房建设与发展