GROUP BY 子句结合 MAX()、MIN() 或 COUNT() 等聚合函数,可帮助从表中去除重复记录。通过 GROUP BY,您可以选择需要保留的记录并删除其他重复项。
假设您要删除重复的客户记录,但保留 ID 最大的一条。可结合 GROUP BY 与 MAX(),如下所示。
-- Delete duplicate rows from the 'customers' table (aliased as c1)DELETE c1
FROM customers c1
-- Find the maximum ID for each unique NameJOIN(SELECT Name,MAX(ID)AS MaxID
FROM customers
GROUPBY Name
) c2
-- Match rows based on 'Name' and keep the row with the maximum IDON c1.Name = c2.Name
AND c1.ID < c2.MaxID;技术支持
MySQL 与 SQL Server 支持上述结合聚合函数与 JOIN 的 GROUP BY 语法。
将 ROW_NUMBER() 与公用表表达式(CTE)结合
结合 ROW_NUMBER() 与 CTE,可以按自定义规则筛除重复。ROW_NUMBER 与 PARTITION BY、ORDER BY 搭配时,会为每行分配唯一的序号。此方法允许过滤不符合条件的行。
以下查询识别重复并保留首次出现,删除其余。
-- Common Table Expression (CTE) to rank rows based on 'Name'WITH CTE AS(SELECT ID, Name, ROW_NUMBER()OVER(PARTITIONBY Name ORDERBY ID ASC)AS RowNum
FROM customers
)-- Select only the unique records where RowNum = 1SELECT ID, Name
FROM CTE
WHERE RowNum =1;技术支持
-- Common Table Expression (CTE) to rank rows based on 'Name'WITH CTE AS(SELECT ID, Name, ROW_NUMBER()OVER(PARTITIONBY Name ORDERBY ID ASC)AS RowNum
FROM customers
)-- Delete rows from the 'customers' table where the row number is greater than 1DELETEFROM customers
WHERE ID IN(SELECT ID FROM CTE WHERE RowNum >1);技术支持
将 DELETE 与子查询结合
有时,使用带子查询的简单 DELETE 即可删除数据库中的重复。这种方法适用于较旧版本的 MySQL 或 PostgreSQL(缺少 ROW_NUMBER() 时)。
下面的查询会删除 customers 表中每个 Name 非最小 ID 的行,仅保留每个唯一 Name 对应的最小 ID 的记录。
-- Delete rows from the 'customers' tableDELETEFROM customers
WHERE ID NOTIN(-- Subquery to find the minimum ID for each unique NameSELECTMIN(ID)FROM customers
GROUPBY Name
);技术支持
结合 HAVING 子句使用 GROUP BY
当需要检查特定列上的重复值时,可结合 GROUP BY 与 HAVING 子句识别重复。此方法允许基于给定条件删除特定行,兼容 SQL Server、MySQL 与 PostgreSQL。
以下查询先识别出现超过一次的姓名,再删除重复,同时为每个 Name 保留 ID 最小的行。
-- Step 1: Identify which Names have duplicatesSELECT Name,COUNT(*)AS duplicate_count
FROM customers
GROUPBY Name
HAVINGCOUNT(*)>1;-- Step 2: Delete duplicate rows, keeping the smallest ID for each NameDELETEFROM customers
WHERE ID NOTIN(SELECTMIN(ID)FROM customers
GROUPBY Name
);技术支持
使用临时表进行批处理
对于大型数据集,临时表有助于高效批处理去重。当单条查询可能引发性能问题时,此方法尤为有效。下面的查询创建一个临时表,存储每个 Name 的最小 ID,并删除 customers 表中不在 temp_customers 中的行。
-- Create a temporary table with unique recordsCREATETEMPORARYTABLE temp_customers ASSELECTMIN(ID)AS KeepID, Name
FROM customers
GROUPBY Name;-- Delete duplicates not in the temporary tableDELETEFROM customers
WHERE ID NOTIN(SELECT KeepID FROM temp_customers);-- Clean upDROPTABLE temp_customers;技术支持
上述使用 CREATE TEMPORARY TABLE 的语法仅支持 MySQL 与 PostgreSQL。
在 SQL Server 中去重
SQL Server 提供多种删除数据库重复记录的方法,包括结合 DISTINCT 与 INTO、使用 ROW_NUMBER(),以及使用临时表。
-- Select distinct rows from 'customers' and create a new table 'unique_customers'SELECTDISTINCT*INTO unique_customers
FROM customers;-- Drop the original 'customers' table to remove it from the databaseDROPTABLE customers;-- Rename the 'unique_customers' table to 'customers' to replace the original tableEXEC sp_rename 'unique_customers','customers';技术支持
使用 ROW_NUMBER()
您也可以使用 ROW_NUMBER() 删除 SQL Server 中的重复记录。假设 Customers 表按 CustomerName 存在重复,且您希望对每个重复组仅保留首次出现。
-- Common Table Expression (CTE) to assign a row number to each customer WITH CTE AS(SELECT CustomerID, CustomerName, ROW_NUMBER()OVER(PARTITIONBY CustomerName ORDERBY CustomerID ASC)AS RowNum
FROM Customers
)-- Delete rows from the CTEDELETEFROM CTE
WHERE RowNum >1;技术支持
使用临时表
由于 SQL Server 不支持 CREATE TEMPORARY TABLE,可使用 SELECT INTO。SQL Server 的临时表以 # 作为表名前缀。
-- Create a temporary tableSELECTMIN(CustomerID)AS ID, CustomerName
INTO#temp_customersFROM customers
GROUPBY CustomerName;-- Delete rows from the 'customers' table where the ID is not in the temporary tableDELETEFROM customers
WHERE CustomerID NOTIN(SELECT ID FROM#temp_customers);-- Optionally drop the temporary table after useDROPTABLE#temp_customers;技术支持
您可以使用 ROW_NUMBER() 搭配 DELETE、带子查询的 DELETE、带 HAVING 的 GROUP BY,以及通过临时表进行批处理等方法,永久删除数据库中的重复行。
重复数据会影响我的数据库性能吗?⌄
会。重复数据会增加存储成本、拖慢查询速度,并使数据分析更复杂,从而对性能产生负面影响。
如何在 SQL 中查找重复行?⌄
使用 GROUP BY 搭配 HAVING COUNT(*) > 1 查找重复。例如:SELECT Name, COUNT(*) FROM customers GROUP BY Name HAVING COUNT(*) > 1; 会返回所有出现超过一次的姓名。您也可以使用 ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ID) 为每个重复组内的行赋予排名——排名大于 1 的行即为重复。
从大型 SQL 表中去重的最快方法是什么?⌄
对于大表,使用临时表方案更快:先用 SELECT DISTINCT 或 GROUP BY 将唯一行插入临时表,截断原表后再将干净数据写回。这避免了对数百万行逐行删除的低效。或者,如需精细控制保留哪条重复,使用带 CTE 的 ROW_NUMBER() 也很高效。无论哪种方式,都应先备份数据,并在预生产环境充分测试。
如何在 SQL 中查找重复行?⌄
使用 GROUP BY 搭配 HAVING COUNT(*) > 1 查找重复。例如:SELECT Name, COUNT(*) FROM customers GROUP BY Name HAVING COUNT(*) > 1; 会返回所有出现超过一次的姓名。您也可以使用 ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ID) 为每个重复组内的行赋予排名——排名大于 1 的行即为重复。
重复记录是常见问题,会损害数据完整性并拖慢数据库性能。去除这些重复行对于保持数据准确、优化存储,以及提升查询性能至关重要。本文将针对不同使用场景与数据库管理系统,介绍在 SQL 中删除重复行的多种技术。
在开始之前,强烈建议您学习 DataCamp 的Introduction to SQL 和 Learn SQL 课程,夯实用 SQL 提取与分析数据的基础知识。此外,我认为可下载的 SQL Basics Cheat Sheet 十分实用,涵盖最常用的 SQL 函数,便于随时查阅。
要点速览
使用
SELECT DISTINCT或GROUP BY获取唯一行,而不修改数据表结合 CTE 使用
ROW_NUMBER()与DELETE,可精确控制永久删除哪些重复行在任意 DBMS 中,可用带子查询的
DELETE(NOT IN/MIN())实现直接的去重针对大数据集,使用临时表分批安全地处理去重
通过主键、唯一约束与合理的数据库规范化,主动预防重复
理解 SQL 中的重复行
SQL 中的重复行指表内在全部或部分选定列上取值相同的记录。造成重复行的常见原因包括:
在接下来的内容中,我们将介绍如何在 SQL 中去重,分为两部分:第一部分讲解如何在为报告或仪表盘检索数据时去重;第二部分讲解如何在数据库中执行去重。
如何识别重复行
在删除重复之前,先识别哪些行是重复的。使用
GROUP BY搭配HAVING COUNT(*) > 1可找出出现超过一次的行:该查询返回每个出现超过一次的
Name及其出现次数。您也可以通过在SELECT与GROUP BY子句中同时添加多列,将其扩展到多列组合。若想为每个重复分组中的行标注其位置,可使用
ROW_NUMBER():row_num > 1的行即为重复。识别后,可从下文选择合适的删除方法。在检索数据时去重的方法
在 SQL 检索记录时,有多种去重方法。每种方法与所用 DBMS(如 SQL Server、MySQL、PostgreSQL)相关。本节将介绍这些方法,并指出各数据库的注意事项。请注意,这些方法仅过滤数据以返回唯一记录,不会修改底层数据表。
使用 DISTINCT 关键字
DISTINCT关键字用于SELECT语句,检索唯一行。针对 MySQL、PostgreSQL 与 SQL Server,其去重语法基本一致。下面的查询将从customers表中检索唯一的客户姓名。结合聚合函数使用 GROUP BY
GROUP BY子句结合MAX()、MIN()或COUNT()等聚合函数,可帮助从表中去除重复记录。通过GROUP BY,您可以选择需要保留的记录并删除其他重复项。假设您要删除重复的客户记录,但保留 ID 最大的一条。可结合
GROUP BY与MAX(),如下所示。MySQL 与 SQL Server 支持上述结合聚合函数与
JOIN的GROUP BY语法。将 ROW_NUMBER() 与公用表表达式(CTE)结合
结合
ROW_NUMBER()与 CTE,可以按自定义规则筛除重复。ROW_NUMBER与PARTITION BY、ORDER BY搭配时,会为每行分配唯一的序号。此方法允许过滤不符合条件的行。以下查询识别重复并保留首次出现,删除其余。
此方法适用于现代版本的 SQL Server、MySQL 和 PostgreSQL。对于大型数据集或更复杂的条件尤为有用,因为它允许精确指定要保留的重复项。
使用自连接(self-JOIN)去重
自连接可以将表与自身进行比较,便于基于特定条件识别并删除重复行。下面的示例通过自连接删除较大的 ID,仅保留每个姓名的首次出现。
上述方法适用于主流数据库,包括 SQL Server、MySQL 和 PostgreSQL。想进一步学习如何用聚合函数与连接筛选数据,请查看我们的 Intermediate SQL 课程。
在数据库中去重的方法
除了在查询中去重,您也可以从数据库中永久删除重复记录。这对于维持数据质量非常重要。以下方法可用于在数据库中去重。
使用 ROW_NUMBER() 与 DELETE
ROW_NUMBER()会在定义的分区内为行分配序号。结合DELETE使用时,可基于特定列对行进行排序并打分,从而识别重复并移除不需要的记录。此方法适用于现代版本的 MySQL(8.0 起)、PostgreSQL 和 SQL Server。假设您希望基于
Name去重,仅保留首次出现(ID最小)的记录:将 DELETE 与子查询结合
有时,使用带子查询的简单
DELETE即可删除数据库中的重复。这种方法适用于较旧版本的 MySQL 或 PostgreSQL(缺少ROW_NUMBER()时)。下面的查询会删除
customers表中每个Name非最小ID的行,仅保留每个唯一Name对应的最小ID的记录。结合 HAVING 子句使用 GROUP BY
当需要检查特定列上的重复值时,可结合
GROUP BY与HAVING子句识别重复。此方法允许基于给定条件删除特定行,兼容 SQL Server、MySQL 与 PostgreSQL。以下查询先识别出现超过一次的姓名,再删除重复,同时为每个
Name保留ID最小的行。使用临时表进行批处理
对于大型数据集,临时表有助于高效批处理去重。当单条查询可能引发性能问题时,此方法尤为有效。下面的查询创建一个临时表,存储每个
Name的最小ID,并删除customers表中不在temp_customers中的行。上述使用
CREATE TEMPORARY TABLE的语法仅支持 MySQL 与 PostgreSQL。在 SQL Server 中去重
SQL Server 提供多种删除数据库重复记录的方法,包括结合
DISTINCT与INTO、使用ROW_NUMBER(),以及使用临时表。结合 DISTINCT 与 INTO
您可以在
SELECT语句中使用DISTINCT创建仅含唯一记录的新表。在确认新表记录无误后,可删除旧表。下面示例从customers表生成去重后的unique_customers表。使用 ROW_NUMBER()
您也可以使用
ROW_NUMBER()删除 SQL Server 中的重复记录。假设Customers表按CustomerName存在重复,且您希望对每个重复组仅保留首次出现。使用临时表
由于 SQL Server 不支持
CREATE TEMPORARY TABLE,可使用SELECT INTO。SQL Server 的临时表以#作为表名前缀。建议您学习我们的 SQL Server Fundamentals 技能路径,以提升表连接与数据分析能力。SQL Server Developer 职业路径将帮助您掌握在 SQL Server 中编写、排错与优化查询的技能。
速查表:SQL 去重方法
下表汇总了本文涉及的所有去重方法,便于您快速选择适合自身场景的方案。
SELECT DISTINCTGROUP BY+ 聚合ROW_NUMBER()+ CTE(SELECT)ROW_NUMBER()+ CTE(DELETE)DELETE搭配子查询NOT IN/MIN()去重JOIN+DELETE#temp)SELECT DISTINCT INTO最佳实践
重复行是影响数据质量与数据库性能的常见问题。请参考以下最佳实践,防止重复记录被插入数据库。
结语
识别并删除重复行对于保持数据库高效与数据准确至关重要。进行修改前请务必备份数据,以避免意外的数据丢失。
如果您希望成为一名熟练的数据分析师,欢迎查看我们的 Associate Data Analyst in SQL 职业路径,学习必备技能。若您想学习如何使用 SQL 构建专业仪表盘,也可选修 Reporting in SQL 课程。最后,建议获取 SQL Associate Certification 认证,以展示您在 SQL 数据分析方面的掌握程度,并在数据从业者中脱颖而出。