【问题标题】:Remove duplicate rows - Impossible to find a decisive answer删除重复行 - 无法找到决定性的答案
【发布时间】:2011-02-15 00:48:38
【问题描述】:

你会立即认为我是直接跑到这里来问我的问题,但我在谷歌上搜索了很多都没有找到决定性的答案。

事实:我有一个包含 330 万行、20 列的表。 第一行是唯一的主键。

我必须删除第 2 列到第 11 列重复的所有行。实际上是一个基本问题,但方法却大不相同,而每个人最终都寻求相同的解决方案,删除重复项。

我个人在考虑 GROUP BY HAVING COUNT(*) > 1 这是要走的路还是你有什么建议?

提前非常感谢! 升

【问题讨论】:

  • 我认为你的意思是关于 PK 的专栏,但是,如果你明白了,我们明白了;)。
  • 我假设您的意思是第 2 列到第 11 列?
  • @Kenneth - 好问题。 @laurens如果您的意思是列平均每组有多少重复项?需要删除的表的百分比将是一个因素。相关:stackoverflow.com/questions/18932/…
  • 首先,感谢所有的答案。我将立即开始测试所有这些,但显然 CTE 是要走的路。当然我的意思是 column2 到 column11 而不是 row,愚蠢的错误!

标签: sql sql-server sql-server-2005 tsql sql-server-2008


【解决方案1】:

作为一个通用答案:

WITH cte AS (
  SELECT ROW_NUMBER() OVER (
      PARTITION BY <groupbyfield> ORDER BY <tiebreaker>) as rn
  FROM Table)
DELETE FROM cte
WHERE rn > 1;

我发现这比 GROUP BY ... HAVING 更强大、更灵活。事实上, GROUP BY ... HAVING 只为您提供重复项,您仍然需要在重复项中选择“守门员”的“琐碎”任务。

ROW_NUMBER OVER (...) 可以更好地控制如何区分重复项(决胜局),并允许诸如“保留前 3 个重复项”之类的行为,而不仅仅是“仅保留 1 个”,这确实是一种行为很难使用 GROUP BY ... HAVING。

您问题的另一部分是如何处理 330 万行。嗯,3.3M 并不是真的那么 大,但我仍然建议分批进行。一次删除 TOP 10000,否则你会将一个巨大的事务推送到日志中,并且可能会压倒你的日志驱动器。

最后一个问题是这是否可以接受。这取决于您的架构。如果 ROW_NUMBER() 必须扫描整个表并假脱机进行计数,并且您必须分批重复此操作 N 次,则它不会执行。适当的索引会有所帮助。但我不能再说什么,不知道所涉及的确切架构(聚集索引/堆的结构,所有非聚集索引等)。

【讨论】:

  • 我想投票,但声望不够……!无论如何,这就是你所需要的, 可以像我一样用更多的列进行扩展谢谢!!
  • WITH CTE AS ( SELECT ROW_NUMBER() OVER ( PARTITION BY [VIN],[DATE],[MILEAGE],[OPERATION],[SLAnr],[SiteID],[RO] ORDER BY [ VIN]) as rn FROM [AD_MASTER].[dbo].[AS_VIN_HISTORY]) DELETE FROM cte WHERE rn > 1;
【解决方案2】:

按您希望唯一的字段分组,并为您的 pk 字段获取聚合值(如 min)。然后将这些结果插入到新表中。

【讨论】:

  • +1,可能不需要将它们插入新表,具体取决于您的最终结果:)
  • 我会将它放在一个新表中以进行调试,然后重命名或存档现有表。如果不与用户测试结果并获得他们的批准,我不会删除这些行。
【解决方案3】:

如果您有 SQL Server 2005 或更新版本,那么最简单的方法是使用 CTE(通用表表达式)。

您需要知道您希望通过哪些标准“分区”您的数据 - 例如创建被认为是相同/重复的数据分区 - 然后您需要按某些东西对这些分区进行排序 - 例如序列 ID、日期/时间或其他内容。

您没有提供有关您的表格的太多详细信息 - 所以让我给您一个示例:

;WITH Duplicates AS
(
   SELECT  
       OrderID,
       ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) AS RowN
   FROM
       dbo.Orders
)
DELETE FROM dbo.Orders
WHERE RowN > 1

CTE (WITH ... AS :...) 为您提供下一条 SQL 语句的“内联视图” - 它不是持久化的或任何东西 - 它只是为下一条语句而存在,然后它就消失了。

基本上,我是按 CustomerID 对我的订单进行“分组”(分区),并按 OrderDate 进行排序。因此,对于每个 CustomerID,我都会得到一个新的“组”数据,它会获得一个从 1 开始的行号。ORDER BY OrderDate DESC 为每个客户提供了最新订单 RowN = 1 值 - 这是我保留的一个订单。

根据 CTE(WITH..... 表达式)删除每个客户的所有其他订单。

显然,您需要根据自己的情况进行调整 - 但是带有 PARTITION BYROW_NUMBER() 的 CTE 是一种非常可靠且简单的技术来消除重复。

【讨论】:

    【解决方案4】:

    如果您不想处理新表删除,则只需使用 DELETE TOP(1)。使用子查询获取重复行的所有 id,然后使用 delete top 删除多行的位置。如果有多个重复项,您可能需要运行多次,但您明白了。

    DELETE TOP(1) FROM Table
    WHERE ID IN (SELECT ID FROM Table GROUP BY Field HAVING COUNT(*) > 1)
    

    希望您能明白这一点。这只是一些帮助演示的伪代码。

    【讨论】:

    • 如果你使用DELETE TOP(1) FROM Table,我也会确保有一个适当的ORDER BY 子句!否则TOP 指的是什么??
    • 是的,我想因为不是所有的列都是一样的,你会想要那个。我只是在考虑重复意味着所有数据都是相同的。但事实并非如此。感谢您提及。
    猜你喜欢
    • 2017-09-13
    • 1970-01-01
    • 1970-01-01
    • 2018-12-08
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多