【问题标题】:Delete duplicates from large dataset (>100Mio rows)从大型数据集中删除重复项(>100Mio 行)
【发布时间】:2013-08-27 09:47:10
【问题描述】:

我知道这个话题在此之前已经多次出现,但没有一个建议的解决方案适用于我的数据集,因为我的笔记本电脑由于内存问题或存储空间已满而停止计算。

我的表如下所示,并且有 108 Mio 行:

Col1       |Col2   |  Col3           |Col4   |SICComb |  NameComb 

Case New   |3523   |  Alexander      |6799   |67993523| AlexanderCase New 
Case New   |3523   |  Undisclosed    |6799   |67993523| Case NewUndisclosed 
Undisclosed|6799   |  Case New       |3523   |67993523| Case NewUndisclosed 
Case New   |3523   |  Undisclosed    |6799   |67993523| Case NewUndisclosed 
SmartCard  |3674   |  NEC            |7373   |73733674| NECSmartCard 
SmartCard  |3674   |  Virtual NetComm|7373   |73733674| SmartCardVirtual NetComm 
SmartCard  |3674   |  NEC            |7373   |73733674| NECSmartCard

唯一的列是SICCombNameComb。我尝试添加主键:

ALTER TABLE dbo.test ADD ID INT IDENTITY(1,1)

但在新的几分钟内,我的存储空间已超过 30 GB。

从表中删除重复项最快和最有效的方法是什么?

【问题讨论】:

  • @user2713440 你对重复的定义是什么?当所有列都相同时?或者当 SICComb 和 NameComb 相同时?
  • 当 SICComb 和 NameComb 相同时。
  • 那你如何决定保留哪一个?

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


【解决方案1】:

如果您使用的是 SQL Server,则可以使用从公用表表达式中删除:

with cte as (
    select row_number() over(partition by SICComb, NameComb order by Col1) as row_num
    from Table1
)
delete
from cte
where row_num > 1

这里所有的行都会被编号,对于SICComb + NameComb 的每个独特组合,您都有自己的序列。您可以通过在over 子句中选择order by 来选择要删除的行。

【讨论】:

  • @ShahgholiArdalan 不要碰我的代码。我相信在 SQL 中使用大写字母的传统应该消失,我总是格式化我的代码以提高可读性,不要让我的答案变得更糟!
  • 这似乎比从具有大量列和行以及包含唯一标识符的列中删除重复项的公认答案更好。
  • "并非所有查询都受益,有些查询可能会大幅倒退。可能取决于您的索引是否对齐。通常对齐索引的访问速度稍慢。有时访问所有分区索引 b-tree查找键。SORT / TOP 查询可能会非常缓慢。当搜索键未分区为前导段时” - sqlsaturday.com
【解决方案2】:

一般来说,从表中删除重复项的最快方法是将记录(没有重复项)插入到临时表中,截断原始表并将它们重新插入。

下面是这个想法,使用 SQL Server 语法:

select distinct t.*
into #temptable
from t;

truncate table t;

insert into t
    select tt.*
    from #temptable;

当然,这在很大程度上取决于第一步的速度。而且,您需要有空间来存储同一张表的两个副本。

请注意,创建临时表的语法因数据库而异。有些使用create table as 的语法而不是select into

编辑:

您的身份插入错误很麻烦。我认为您需要从不同的列列表中删除身份。或者这样做:

select min(<identity col>), <all other columns>
from t
group by <all other columns>

如果您有一个标识列,则没有重复项(根据定义)。

最后,您需要决定要为行使用哪个 id。如果您可以为行生成一个新的 id,那么只需将标识列从插入的列列表中移除:

insert into t(<all other columns>)
    select <all other columns>;

如果您需要旧的身份值(并且最小值就可以),请关闭身份插入并执行以下操作:

insert into t(<all columns including identity>)
    select <all columns including identity>;

【讨论】:

  • +1,我喜欢这个解决方案,因为它很干净并且是最佳实践。但我还要添加一条评论,在执行此过程以收集所有信息/统计数据之前,可能是重复的数量很少而且更快/更好地删除它们而不是插入数百万行来回......跨度>
  • @MrSimpleMind 。 . .你是对的。如果只有少数重复项(例如数百万行的表中的数千个),最好直接使用delete
  • @Gordon Linoof:感谢您的回答。不幸的是,它显示我的 identify_INSERT 没有打开。运行代码的结果是一个空表。
  • @Gordon Linoof:您的编辑很有帮助。但是现在弹出错误消息“列名或提供的值的数量与表定义不匹配”。
  • @user2713440 。 . .您需要确保insert 的列列表和select 之后的列表具有相同的列且顺序相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2017-01-16
  • 2019-05-20
  • 1970-01-01
  • 2021-05-16
  • 2016-05-16
  • 2011-08-08
相关资源
最近更新 更多