重复编写SQL Server表

时间:2014-08-07 01:36:31

标签: sql sql-server deduplication

我有一个问题。我有一个有近20亿行的表(是的,我知道......)并且有很多重复数据,我想从中删除它。我想知道该如何做到这一点?

列是:first,last,dob,address,city,state,zip,telephone,位于名为PF_main的表中。谢天谢地,每条记录都有一个唯一的ID,它在名为ID的列中。

如何重复删除此项并在pf_main表格中为每个人留下1个唯一条目(行)?

提前感谢您的回复...

5 个答案:

答案 0 :(得分:6)

SELECT 
   ID, first, last, dob, address, city, state, zip, telephone, 
   ROW_NUMBER() OVER (PARTITION BY first, last, dob, address, city, state, zip, telephone ORDER BY ID) AS RecordInstance
FROM PF_main

将为您提供每个唯一条目的“编号”(按ID排序)

所以,如果您有以下记录:

  

id,last,first,dob,address,city,state,zip,telephone
  006,trevelyan,alec,'1954-05-15','85 Albert Embankment','London',   'UK','1SE1 7TP',0064
  007,邦德,詹姆斯,'1957-02-08','85 Albert Embankment','伦敦',   'UK','1SE1 7TP',0074
  008,邦德,詹姆斯,'1957-02-08','85 Albert Embankment','伦敦',   'UK','SE1 7TP',0074
  009,邦德,詹姆斯,'1957-02-08','85 Albert Embankment','伦敦',   'UK','SE1 7TP',0074

您将获得以下结果(请注意最后一栏)

  006,trevelyan,alec,'1954-05-15','85 Albert Embankment','London',   'UK','1SE1 7TP',0064, 1
  007,邦德,詹姆斯,'1957-02-08','85 Albert Embankment','伦敦',   'UK','1SE1 7TP',0074, 1
  008,邦德,詹姆斯,'1957-02-08','85 Albert Embankment','伦敦',   'UK','SE1 7TP',0074, 2
  009,邦德,詹姆斯,'1957-02-08','85 Albert Embankment','伦敦',   'UK','SE1 7TP',0074, 3

所以你可以用RecordInstance>删除记录。 1:

WITH Records AS
(
   SELECT 
      ID, first, last, dob, address, city, state, zip, telephone,
      ROW_NUMBER() OVER (PARTITION BY first, last, dob, address, city, state, zip, telephone ORDER BY ID) AS RecordInstance
   FROM PF_main
)
DELETE FROM Records
WHERE RecordInstance > 1

答案 1 :(得分:3)

其他答案肯定会为你提供语法方面的想法。

有20亿行,你的问题可能涉及除语法之外的其他事情,所以我会给你一个适用于许多数据库的通用答案。如果您负担不起删除或复制" online"在一个会话中,或者空间不足,请考虑以下增量方法。

删除此大型文件可能需要很长很长时间才能完成,例如几小时甚至几天,并且还有在完成之前失败的风险。在少数情况下,令人惊讶的是,最好的方法是一个基本的,长期运行的存储过程,它采用小批量并且每几个记录(很少是这里的相对术语)。很少有100或1000或10000记录。当然它看起来并不优雅,但关键是它是"增量"和资源消耗低的人。

这个想法是识别一个分区键,通过该键可以处理记录范围(将工作区分区),或者执行初始查询以识别另一个表的重复键。然后一次遍历这些键一小批,删除,然后提交,然后重复。如果在没有临时表的情况下进行实际操作,请确保通过添加适当的条件来缩小范围以减少结果集,并保持光标或排序区域大小。

-- Pseudocode
counter = 0;
for each row in dup table       -- and if this takes long, break this into ranges
   delete from primary_tab where id = @id
   if counter++ > 1000 then
      commit;
      counter = 0;
   end if
end loop

可以停止并重新启动此存储过程,而无需担心巨大的回滚,并且它还可以可靠地运行数小时或数天而不会对数据库可用性产生重大影响。在Oracle中,这可能是还原段和排序区域大小以及其他东西,但在MSSQL中我不是专家。最终,它将完成。与此同时,您不会使用锁或大事务来绑定目标表,因此DML可以在表上继续。需要注意的是,如果DML继续存在,那么您可能必须将快照重复到dup ids表以处理自快照以来出现的任何重复。

警告:这不会对自由块/行进行碎片整理或合并已删除的空间,例如完全构建新表,但它确实允许在线完成并且不分配新副本。另一方面,如果您可以自由地在线和/或维护窗口,并且重复的行大于您的数据的15-20%,那么您应该选择"创建表作为选择*从原始减去重复"方法,如戈登的答案,为了将数据段压缩成密集使用的连续段,并从长远来看获得更好的缓存/ IO性能。然而,很少有重复超过空间的一小部分。

这样做的原因包括:

  

1 - 表太大而无法创建临时的去除副本。

     

2 - 一旦新的表格准备好,您就不能或不想删除原始表格。

     

3 - 您无法通过维护窗口进行一次大型操作。

否则,请参阅Gordon Linoff的回答。

答案 2 :(得分:3)

20亿行表非常大。我假设firstlastdob构成一个" person"。我的建议是建立一个人的指数"然后执行truncate /重新插入方法。

在实践中,这看起来像:

create index idx_pf_main_first_last_dob on pf_main(first, last, dob);

select m.*
into temp_pf_main
from pf_main m
where not exists (select 1
                  from pf_main m2
                  where m2.first = m.first and m2.last = m.last and m2.dob = m.dob and
                        m2.id < m.id
                 );

truncate table pf_main;

insert into pf_main
    select *
    from temp_pf_main;

答案 3 :(得分:1)

其他人在技术方面提供了很好的方法,我只想添加一个实用的观点。

恕我直言,很难完全自动化消除大型人员表中重复项的过程。如果匹配过于宽松......那么合法记录将被删除。如果匹配太严格......那么将留下重复的内容。

对于我的客户,我构建了一个类似上面的查询,它返回表示LIKELY重复项的行,使用Address和Last Name作为匹配条件。然后他们会注意可能的列表,然后在他们选择复制的行上单击“删除”或“合并”。

这可能不适用于您的项目(具有数十亿行),但在需要避免重复数据和丢失数据的环境中这是有意义的。单个人工操作员可以在几分钟内提高数千行数据的清洁度,并且他们可以在多个会话中一次执行一次。

答案 4 :(得分:0)

恕我直言,没有一种最佳的重复数据删除方法,这就是为什么你会看到这么多不同的解决方案。这取决于你的情况。现在,我的情况是,我有一个很大的历史档案,列出了数万个贷款账户中每年和每年的每月指标。每个帐户都会在文件中显示多个月末,同时保持活动状态,但当它变为非活动状态时,它将在以后出现。我只想要每个帐户的最后或最新记录。我不关心20年前帐户开通时的记录,我关心的是5年前帐户关闭时的最新记录。对于仍处于活动状态的帐户,我希望记录最近的日历月。因此,我认为“重复”是除了该帐户的最后一个月记录以外的所有记录,我希望摆脱它们。

这可能不是您的确切问题,但我提出的解决方案可能会为您提供您想要的解决方案。

(我在SAS PROC SQL中执行了大部分的sql代码,但我认为你会明白这一点。)

我的解决方案使用子查询...

 /* dedupe */ 
 proc sql ;   
   create table &delta. as       
     select distinct b.*, sub.mxasof
       from &bravo. b
       join ( select distinct Loan_Number, max(asof) as mxasof format 6.0
                from &bravo.
                group by Loan_Number
            ) sub
       on 1
       and b.Loan_Number = sub.Loan_Number
       and b.asof = sub.mxasof
       where 1
       order by b.Loan_Number, b.asof desc ;