2010-08-17 37 views
6

在搜索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 Ponies的方法,僅用了40分鐘就完成了。 我嘗試了第2步Andomar的批量刪除,它運行了9個小時,然後我停止了它。 更新: 使用OMG Ponies的方法,使用少一個字段的類似查詢來擺脫不同的重複集合,並且查詢僅運行4分鐘(8000行)。

我會嘗試下cte技術,但是,我懷疑OMG小馬的方法很難擊敗。

+1

A到你的查詢夫妻容易的優化上面 - 你不必有A,B,C等頂層查詢的'SELECT' - 你只需要PriKey,並刪除HAVING - 那麼,在第二個查詢只是'DELETE FROM sourceTable WHERE PriKey NOT IN(SELECT DT.MaxPriKey FROM #dupTemp DT)' – 2010-08-17 22:30:49

+0

感謝您的提示。 – 2010-08-17 22:35:40

回答

4

什麼存在:

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) 
+0

請解釋你爲什麼認爲這種方式會更快。 – 2010-08-17 22:13:44

+1

@ sub13:EXISTS與JOIN或IN不同 - 它在條件的第一次匹配時返回true。理論是更少的工作應該等於更快的查詢。在相關說明中,[本文](http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/)將解釋和對比一些選項。 – 2010-08-17 22:16:18

+0

EXISTS()中的所有列都必須是非空的嗎? – 2010-08-17 22:31:38

0

好很多不同的事情。首先會是這樣的工作(做一個選擇o確保,甚至投入到它自己的臨時表,#recordsToDelete):

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

接下來,您可以索引臨時表,把一個指數prikey

如果您在臨時表中有要刪除的記錄,可以批量刪除,這通常比使用刪除整個表的速度更快。

+0

處理非空列時,NOT IN和NOT EXISTS效率更高:http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left- join-is-null-sql-server/ – 2010-08-17 22:11:10

3

大容量行刪除的瓶頸通常是SQL Server必須建立的事務。通過將拆除拆分爲更小的交易,您可能可以大幅提高速度。例如,要一次刪除100行:

while 1=1 
    begin 

    delete top 100 
    from sourceTable 
    ... 

    if @@rowcount = 0 
     break 
    end 
+0

這是一個非常有趣的想法。我肯定會試試這個。 – 2010-08-17 22:13:10

+0

順便說一句:我不認爲刪除top 100是有效的語法 – 2010-08-17 22:59:31

+2

@ subt13:它是 - 請參閱[SQL Server 2008 BOL - 刪除](http://msdn.microsoft.com/en-us/library/ms189835.aspx ) – 2010-08-17 23:11:12

4

您能否承受原始表暫時不可用?

我認爲最快的解決方案是創建一個沒有重複的新表。基本上你使用臨時表的方法,而是創建一個「常規」表。

然後刪除原始表並將中間表重命名爲與舊錶具有相同的名稱。

+0

是的。普通表比臨時表或其他東西快嗎?請原諒我的無知:) – 2010-08-17 22:25:39

+0

可能會成爲迄今爲止提出的最快捷的解決方案 - 如果有外鍵等,如果你不小心,這會變得痛苦和容易出錯,但絕對值得考慮。 – 2010-08-17 22:27:11

+1

@ subt13:你需要常規桌子,因爲你要保留它;)(與你的臨時桌子相反) @WillA:是的,你是對的,需要小心約束。 – 2010-08-17 22:32:00

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 
    ) ; 

順便說一下,你有任何可以暫時刪除的索引嗎?

+1

馬丁史密斯有一天表示,CTE可以被引用爲DELETE源,其功能類似於可更新視圖。 – 2010-08-17 22:17:44

+0

雅,這是一個很酷的功能,我只是不知道效率與舊的時尚#temp表相比。在這麼多行上做任何事情都需要一段時間。我有一個聚集索引。如果需要更多,我當然可以添加它們。 – 2010-08-17 22:24:33

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 
+0

很酷。我以爲我在幫忙,最後我得到了幫助。這比我的建議表現更好。 – bobs 2010-08-17 22:35:51

+0

這非常緊湊,但我對速度更感興趣。從我閱讀和看到的ctes中,他們僅僅是語法糖。然而,如果我錯了,請糾正我。 – 2010-08-17 22:49:15

+0

@ subt13:在比較各種選項之間的實際查詢計劃後,您必須通知我們。 – 2010-08-17 23:12:08