SQL 删除重复行完全指南

重复数据会通过数据导入、应用程序错误或缺少约束等方式潜入数据库。清理重复数据是每个数据专业人员必备的关键技能。

本指南涵盖多种方法,从识别重复数据到安全地删除它们。

步骤 1:查找重复数据

在删除任何数据之前,让我们先识别问题所在。

使用 GROUP BY 统计重复数据

SQL 查询

sql 复制代码
-- 查找哪些邮箱有重复
SELECT 
  email,
  COUNT(*) as occurrence_count
FROM users_duplicates
GROUP BY email
HAVING COUNT(*) > 1;

查询结果示例

email occurrence_count
alice@example.com 2
bob@example.com 3

现在我们知道 alice@example.com 出现了 2 次,bob@example.com 出现了 3 次。

步骤 2:查看所有重复行

使用子查询获取完整的详细信息:

SQL 查询

sql 复制代码
-- 显示所有有重复的行
SELECT * 
FROM users_duplicates2
WHERE email IN (
  SELECT email 
  FROM users_duplicates2
  GROUP BY email
  HAVING COUNT(*) > 1
);

这个查询会返回所有重复记录的完整信息,帮助你决定保留哪一条。

策略 1:保留第一条(或最后一条)记录

最常见的方法是使用 ROW_NUMBER() 来标记要保留的重复记录。

使用 ROW_NUMBER 识别重复数据

SQL 查询

sql 复制代码
-- 标记重复:row_num > 1 表示重复
SELECT 
  *,
  ROW_NUMBER() OVER (
    PARTITION BY email 
    ORDER BY created_at
  ) as row_num
FROM users;

查询结果示例

id email name created_at row_num
1 alice@example.com Alice 2026-01-01 1
2 alice@example.com Alice 2026-01-02 2
3 bob@example.com Bob 2026-01-01 1
4 bob@example.com Bob 2026-01-02 2
5 bob@example.com Bob 2026-01-03 3

说明

  • row_num = 1:第一条记录(保留这条)
  • row_num > 1:重复记录(删除这些)

使用 CTE 删除重复数据

现在我们可以删除重复数据了:

sql 复制代码
-- SQLite 语法删除重复数据
DELETE FROM users
WHERE id IN (
  SELECT id FROM (
    SELECT 
      id,
      ROW_NUMBER() OVER (
        PARTITION BY email 
        ORDER BY created_at
      ) as row_num
    FROM users
  )
  WHERE row_num > 1
);

提示 :将 ORDER BY created_at 改为 ORDER BY created_at DESC 可以保留最新的记录而不是最早的。

策略 2:保留数据最完整的记录

有时你想保留"最完整"的记录:

sql 复制代码
-- 保留名字最长的记录(数据最多)
SELECT id FROM (
  SELECT 
    id,
    ROW_NUMBER() OVER (
      PARTITION BY email 
      ORDER BY LENGTH(name) DESC
    ) as row_num
  FROM users
)
WHERE row_num > 1;

这个方法适用于某些字段可能为空或不完整的情况。

策略 3:SELECT DISTINCT 到新表

对于重复数据较多的表,创建一个干净的副本通常更容易:

sql 复制代码
-- 创建只包含唯一记录的新表
CREATE TABLE users_clean AS
SELECT DISTINCT email, name
FROM users;

-- 或者保留每个重复组的第一条
CREATE TABLE users_clean AS
SELECT * FROM (
  SELECT 
    *,
    ROW_NUMBER() OVER (
      PARTITION BY email 
      ORDER BY created_at
    ) as rn
  FROM users
)
WHERE rn = 1;

优点

  • 不会影响原表
  • 可以先验证结果
  • 适合大量重复数据的场景

策略 4:聚合重复数据

有时重复数据有不同的值需要合并:

SQL 查询

sql 复制代码
-- 通过求和金额合并重复订单
SELECT 
  customer_id,
  order_date,
  SUM(amount) as total_amount,
  COUNT(*) as merged_count
FROM orders_duplicates
GROUP BY customer_id, order_date;

示例数据

原始数据(orders_duplicates):

customer_id order_date amount
101 2026-01-15 50
101 2026-01-15 30
102 2026-01-16 100

查询结果:

customer_id order_date total_amount merged_count
101 2026-01-15 80 2
102 2026-01-16 100 1

这种方法适用于需要保留数值总和或其他聚合信息的场景。

防止未来出现重复数据

添加 UNIQUE 约束

sql 复制代码
-- 防止邮箱重复
CREATE UNIQUE INDEX idx_unique_email ON users(email);

-- 或在创建表时添加
CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  email TEXT UNIQUE,
  name TEXT
);

使用 INSERT OR IGNORE

sql 复制代码
-- 跳过会创建重复的插入操作
INSERT OR IGNORE INTO users (email, name)
VALUES ('alice@example.com', 'Alice');

这个语句会在遇到重复值时静默跳过,而不是报错。

快速参考:选择哪种策略?

场景 最佳方法
保留最早的记录 ROW_NUMBER() ORDER BY created_at
保留最新的记录 ROW_NUMBER() ORDER BY created_at DESC
保留最完整的记录 ROW_NUMBER() ORDER BY LENGTH(field) DESC
合并数值 GROUP BY 配合 SUM/MAX
大量重复数据 创建新的干净表
防止未来重复 UNIQUE 约束

安全提示

  1. 始终先备份 :在运行 DELETE 之前先运行 SELECT 验证将要删除的内容。

  2. 使用事务 :将删除操作包装在 BEGIN/COMMIT 中,这样如果需要可以 ROLLBACK

sql 复制代码
BEGIN TRANSACTION;
-- 你的删除操作
DELETE FROM users WHERE ...;
-- 检查结果
SELECT COUNT(*) FROM users;
-- 如果正确就提交,否则回滚
COMMIT;
-- ROLLBACK;
  1. 检查外键:删除一行可能会破坏其他表中的引用关系。

  2. 记录已删除的行:在删除之前将它们插入到归档表中。

sql 复制代码
-- 先归档
INSERT INTO users_archive SELECT * FROM users WHERE ...;
-- 再删除
DELETE FROM users WHERE ...;

总结

删除重复数据是一个多步骤的过程:

  1. 识别 重复数据:使用 GROUP BY HAVING COUNT(*) > 1
  2. 检查它们以决定保留哪条
  3. 标记 重复数据:使用 ROW_NUMBER() OVER (PARTITION BY...)
  4. 删除 row_num > 1 的行
  5. 防止 未来重复:使用 UNIQUE 约束

掌握这些模式,你就能处理任何数据去重挑战。


相关文章推荐


本文转载自 www.hisqlboy.com

原文标题:Removing Duplicate Rows in SQL: A Complete Guide

原文链接:https://www.hisqlboy.com/blog/removing-duplicate-rows-sql

原作者:SQL Boy Team

转载日期:2026-02-08

相关推荐
数据组小组18 小时前
免费数据库管理工具深度横评:NineData 社区版、Bytebase 社区版、Archery,2026 年开发者该选哪个?
数据库·测试·数据库管理工具·数据复制·迁移工具·ninedata社区版·naivicat平替
爱可生开源社区20 小时前
MiniMax M2.5 的 SQL 能力令人惊艳!
sql·llm
Nyarlathotep01131 天前
事务隔离级别
sql·mysql
悟空聊架构1 天前
基于KaiwuDB在游乐场“刷卡+投币”双模消费系统中的落地实践
数据库·后端·架构
IvorySQL1 天前
PostgreSQL 技术日报 (3月4日)|硬核干货 + 内核暗流一网打尽
数据库·postgresql·开源
Nyarlathotep01131 天前
SQL的事务控制
sql·mysql
进击的丸子1 天前
虹软人脸服务器版SDK(Linux/ARM Pro)多线程调用及性能优化
linux·数据库·后端
NineData2 天前
NineData智能数据管理平台新功能发布|2026年1-2月
数据库·sql·数据分析
IvorySQL2 天前
双星闪耀温哥华:IvorySQL 社区两项议题入选 PGConf.dev 2026
数据库·postgresql·开源
ma_king2 天前
入门 java 和 数据库
java·数据库·后端