删除重复数据的最快技术

时间:2010-08-17 21:56:15

标签: sql sql-server sql-server-2008 etl

在搜索了stackoverflow.com之后,我发现了几个问题,询问如何删除重复项,但没有一个能解决速度问题。

在我的情况下,我有一个包含10列的表,其中包含500万个确切的行重复项。另外,我在10列中的9列中至少有一百万行具有重复。我目前的技术是(到目前为止) 3小时来删除这500万行。这是我的过程:

-- Step 1:  **This step took 13 minutes.** Insert only one of the n duplicate rows into a temp table
select
    MAX(prikey) as MaxPriKey, -- identity(1, 1)
    a,
    b,
    c,
    d,
    e,
    f,
    g,
    h,
    i
into #dupTemp
FROM sourceTable
group by
    a,
    b,
    c,
    d,
    e,
    f,
    g,
    h,
    i
having COUNT(*) > 1

接下来,

-- Step 2: **This step is taking the 3+ hours**
-- delete the row when all the non-unique columns are the same (duplicates) and
-- have a smaller prikey not equal to the max prikey
delete 
from sourceTable
from sourceTable
inner join #dupTemp on  
    sourceTable.a = #dupTemp.a and
    sourceTable.b = #dupTemp.b and
    sourceTable.c = #dupTemp.c and
    sourceTable.d = #dupTemp.d and
    sourceTable.e   = #dupTemp.e and
    sourceTable.f = #dupTemp.f and
    sourceTable.g = #dupTemp.g and
    sourceTable.h = #dupTemp.h and
    sourceTable.i   = #dupTemp.i and
    sourceTable.PriKey != #dupTemp.MaxPriKey  

有关如何加快速度或更快速的提示吗?请记住,对于不完全重复的行,我将不得不再次运行它。

非常感谢。

更新:
  我不得不停止步骤2从9小时开始跑步。   我尝试了OMG小马的方法,它只用了40分钟就结束了。   我用Andomar的批量删除尝试了我的第2步,它在我停止之前运行了9个小时。 更新:   使用一个较少的字段进行类似的查询以删除不同的重复项集,并且使用OMG Ponies的方法查询仅运行4分钟(8000行)。

我会在下一次机会尝试cte技术,但是,我怀疑OMG小马的方法很难被击败。

7 个答案:

答案 0 :(得分:4)

EXISTS怎么样:

DELETE FROM sourceTable
 WHERE EXISTS(SELECT NULL
                FROM #dupTemp dt
               WHERE sourceTable.a = dt.a 
                 AND sourceTable.b = dt.b 
                 AND sourceTable.c = dt.c 
                 AND sourceTable.d = dt.d 
                 AND sourceTable.e = dt.e 
                 AND sourceTable.f = dt.f 
                 AND sourceTable.g = dt.g 
                 AND sourceTable.h = dt.h 
                 AND sourceTable.i = dt.i 
                 AND sourceTable.PriKey < dt.MaxPriKey)

答案 1 :(得分:4)

您可以在短时间内让原始桌子无法使用吗?

我认为最快的解决方案是创建一个没有重复项的新表。基本上是您使用临时表的方法,而是创建一个“常规”表。

然后删除原始表并将中间表重命名为与旧表同名。

答案 2 :(得分:3)

批量行删除的瓶颈通常是SQL Server必须构建的事务。通过将删除拆分为较小的事务,您可以大大加快速度。例如,要一次删除100行:

while 1=1
    begin

    delete top 100
    from sourceTable 
    ...

    if @@rowcount = 0
        break
    end

答案 3 :(得分:1)

...基于上面的OMG Ponies评论,CTE方法更加紧凑。这种方法可以在您(无论出于何种原因)没有主键的表格上创造奇迹 - 您可以在所有列上拥有相同的行。

;WITH cte AS (
 SELECT ROW_NUMBER() OVER 
          (PARTITION BY a,b,c,d,e,f,g,h,i ORDER BY prikey DESC) AS sequence
    FROM sourceTable
)
DELETE
FROM cte
WHERE sequence > 1

答案 4 :(得分:0)

很多不同的事情。首先会像这样的工作(做一个选择o确保,甚至可能放入它自己的临时表,#recordToDelete):

delete  
from sourceTable 
left join #dupTemp on   
       sourceTable.PriKey = #dupTemp.MaxPriKey   
where #dupTemp.MaxPriKey  is null

接下来,您可以索引临时表,在prikey上放置索引

如果您要删除的临时表中有记录,则可以批量删除,这通常比通过删除锁定整个表更快。

答案 5 :(得分:0)

这是一个可以将两个步骤合并为一个步骤的版本。

WITH cte AS
    ( SELECT prikey, ROW_NUMBER() OVER (PARTITION BY a,b,c,d,e,f,g,h,i ORDER BY
        prikey DESC) AS sequence
    FROM sourceTable
    )

DELETE
FROM sourceTable
WHERE prikey IN
    ( SELECT prikey
    FROM cte
    WHERE sequence > 1
    ) ;

顺便问一下,你有没有可以暂时删除的索引吗?

答案 6 :(得分:0)

如果您使用的是Oracle数据库,我最近发现,从总持续时间以及CPU消耗的角度来看,以下语句的效果最佳。 我已经用循环从几十行到数千行的不同数据大小执行了几次测试。我使用TKProf工具分析结果。

与上面的ROW_NUMBER()解决方案相比,此方法花费了原始时间的2/3,并消耗了大约50%的CPU时间。它似乎表现出线性关系,也就是说,无论任何输入数据大小,它都应给出相似的结果。

请随时给我您的反馈。我想知道是否有更好的方法。

DELETE FROM sourceTable
WHERE
    ROWID IN(
        -- delete all
        SELECT ROWID
        FROM sourceTable t
        MINUS
        -- but keep every unique row
        SELECT
            rid
        FROM
            (
            SELECT a,b,c,d,e,f,g,h,i, MAX(ROWID) KEEP (DENSE_RANK FIRST ORDER BY ROWID) AS RID
            FROM sourceTable t
            GROUP BY a,b,c,d,e,f,g,h,i
            )
    )
;