重复数据会通过数据导入、应用程序错误或缺少约束等方式潜入数据库。清理重复数据是每个数据专业人员必备的关键技能。
本指南涵盖多种方法,从识别重复数据到安全地删除它们。
步骤 1:查找重复数据
在删除任何数据之前,让我们先识别问题所在。
使用 GROUP BY 统计重复数据
SQL 查询:
sql
-- 查找哪些邮箱有重复
SELECT
email,
COUNT(*) as occurrence_count
FROM users_duplicates
GROUP BY email
HAVING COUNT(*) > 1;
查询结果示例:
| 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 | 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 约束 |
安全提示
-
始终先备份 :在运行
DELETE之前先运行SELECT验证将要删除的内容。 -
使用事务 :将删除操作包装在
BEGIN/COMMIT中,这样如果需要可以ROLLBACK。
sql
BEGIN TRANSACTION;
-- 你的删除操作
DELETE FROM users WHERE ...;
-- 检查结果
SELECT COUNT(*) FROM users;
-- 如果正确就提交,否则回滚
COMMIT;
-- ROLLBACK;
-
检查外键:删除一行可能会破坏其他表中的引用关系。
-
记录已删除的行:在删除之前将它们插入到归档表中。
sql
-- 先归档
INSERT INTO users_archive SELECT * FROM users WHERE ...;
-- 再删除
DELETE FROM users WHERE ...;
总结
删除重复数据是一个多步骤的过程:
- 识别 重复数据:使用
GROUP BY HAVING COUNT(*) > 1 - 检查它们以决定保留哪条
- 标记 重复数据:使用
ROW_NUMBER() OVER (PARTITION BY...) - 删除
row_num > 1的行 - 防止 未来重复:使用
UNIQUE约束
掌握这些模式,你就能处理任何数据去重挑战。
相关文章推荐
- Data Cleaning with SQL: From Messy to Masterpiece - 真实世界的数据是脏的。掌握 4 个基本的 SQL 函数来清理数据
- SQL Window Frames: ROWS vs RANGE - 了解 ROWS 和 RANGE 窗口帧如何改变结果
- Mastering Slowly Changing Dimensions (SCD Type 2) in SQL - 数据会变化,但历史很重要。学习如何跟踪变化
本文转载自 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