2010-08-17 8 views
6

stackoverflow.com खोजने के बाद मुझे डुप्लिकेट को हटाने का तरीका पूछने के कई प्रश्न मिले, लेकिन उनमें से कोई भी गति को संबोधित नहीं किया।डुप्लिकेट डेटा हटाने के लिए सबसे तेज़ तकनीक

मेरे मामले में मेरे पास 10 कॉलम वाली एक तालिका है जिसमें 5 मिलियन सटीक पंक्ति डुप्लिकेट हैं। इसके अलावा, मेरे पास 10 कॉलम में से 9 में डुप्लीकेट के साथ कम से कम दस लाख अन्य पंक्तियां हैं। मेरी वर्तमान तकनीक इन 5 मिलियन पंक्तियों को हटाने के लिए (अब तक) 3 घंटे ले रही है।

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

यह कैसे गति, या एक तेज़ तरीका पर कोई सुझाव: यहाँ मेरी प्रक्रिया क्या है? याद रखें मुझे इसे उन पंक्तियों के लिए फिर से चलाने की आवश्यकता होगी जो सटीक डुप्लिकेट नहीं हैं।

बहुत बहुत धन्यवाद।

अद्यतन:
मुझे 9 घंटे के निशान पर चलने से चरण 2 को रोकना पड़ा। मैंने ओएमजी पोनीज़ की विधि की कोशिश की और यह केवल 40 मिनट के बाद समाप्त हो गया। मैंने एंडोमर के बैच डिलीट के साथ अपने चरण 2 की कोशिश की, यह इसे रोकने से 9 घंटे पहले चला। अद्यतन: डुप्लिकेट के एक अलग सेट से छुटकारा पाने के लिए एक कम फ़ील्ड के साथ एक समान क्वेरी चलाएं और क्वेरी ओएमजी पोनीज़ विधि का उपयोग करके केवल 4 मिनट (8000 पंक्तियों) के लिए दौड़ गई।

मैं सीटी तकनीक को अगले मौके की कोशिश करूंगा, हालांकि, मुझे संदेह है कि ओएमजी पोनीज़ की विधि को हरा करना मुश्किल होगा।

+1

ऊपर आपके प्रश्नों के लिए आसान अनुकूलन के एक जोड़े - आप शीर्ष क्वेरी की 'SELECT' में एक, ख, ग आदि की जरूरत नहीं है - तुम सिर्फ PriKey की जरूरत है, और होने ड्रॉप - तो , दूसरी क्वेरी में बस 'स्रोत से हटाएं जहां कहीं नहीं है (डीटी.मैक्सप्रैकी को #dupTemp डीटी से चुनें) ' –

+0

टिप के लिए धन्यवाद। –

उत्तर

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

कृपया बताएं कि आप क्यों सोचते हैं कि यह तरीका तेज़ होगा। –

+1

@ उप 13: EXISTS जॉइन या इन से अलग है - यह मानदंड के पहले मैच पर सच होता है। सिद्धांत कम काम एक तेज क्वेरी के बराबर होना चाहिए। संबंधित नोट पर, [यह आलेख] (http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-oin-is-null-sql-server/) कुछ विकल्प समझाएं और विपरीत करें। –

+0

EXISTS() के भीतर सभी कॉलम गैर-शून्य होने की आवश्यकता है? –

0

खैर differnt चीजों के बहुत सारे।

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

इसके बाद आप सूचकांक अस्थायी टेबल, prikey

पर एक सूचकांक रख सकते हैं: सबसे पहले इस काम की तरह कुछ होगा (एक का चयन ओ सुनिश्चित कर लें करते हैं, भी हो सकता है यह खुद की एक अस्थायी मेज, #recordsToDelete में डाल दिया)

यदि आपके पास उन लोगों की एक अस्थायी तालिका में रिकॉर्ड हैं जिन्हें आप हटाना चाहते हैं, तो आप बैच में हटा सकते हैं जो पूरी तालिका को हटाने के साथ लॉक करने से अक्सर तेज़ होता है।

+0

गैर-शून्य कॉलम से निपटने पर, 'इन नहीं' और 'EXISTS' अधिक कुशल नहीं हैं: http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left- join-is-null-sql-server/ –

3

थोक पंक्ति हटाने में बाधा आमतौर पर लेनदेन है जिसे SQL सर्वर को बनाना है। आप छोटे लेनदेन में हटाने को विभाजित करके इसे तेज़ी से तेज करने में सक्षम हो सकते हैं। उदाहरण के लिए, एक समय में 100 पंक्तियां हटाने के लिए:

while 1=1 
    begin 

    delete top 100 
    from sourceTable 
    ... 

    if @@rowcount = 0 
     break 
    end 
+0

यह एक बहुत ही रोचक विचार है। मैं निश्चित रूप से यह कोशिश करूँगा। –

+0

बीटीडब्ल्यू: मुझे नहीं लगता कि शीर्ष 100 को हटाएं वैध वाक्यविन्यास –

+2

@ subt13: यह है - देखें [SQL सर्वर 2008 बीओएल - DELETE] (http://msdn.microsoft.com/en-us/library/ms189835.aspx) –

4

क्या आप मूल तालिका को कम समय के लिए अनुपलब्ध कर सकते हैं?

मुझे लगता है कि सबसे तेज़ समाधान डुप्लीकेट के बिना एक नई तालिका बनाना है। असल में वह दृष्टिकोण जो आप temp तालिका के साथ उपयोग करते हैं, लेकिन इसके बजाय "नियमित" तालिका बनाते हैं।

फिर मूल तालिका को छोड़ दें और पुरानी तालिका के समान नाम रखने के लिए मध्यवर्ती तालिका का नाम बदलें।

+0

हां। एक टेम्पलेट टेबल या कुछ से एक नियमित तालिका तेज है? कृपया मेरी अज्ञानता से क्षमा करें :) –

+0

संभवतया अब तक प्रस्तावित सबसे तेज़ समाधान होने जा रहा है - यदि विदेशी कुंजी हैं तो यह दर्दनाक हो जाता है और यदि आप सावधान नहीं हैं, तो निश्चित रूप से विचार करने के लिए प्रवण हो जाते हैं। –

+1

@ subt13: आपको नियमित तालिका की आवश्यकता है क्योंकि आप इसे रखने जा रहे हैं;) (आपकी temp तालिका के विपरीत) @WillA: हाँ आप सही हैं, किसी को बाधाओं से सावधान रहने की आवश्यकता है। –

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

मार्टिन स्मिथ ने दूसरे दिन दिखाया कि सीटीई को डिलीट स्रोत के रूप में संदर्भित किया जा सकता है, जो एक अद्यतन दृश्य की तरह काम कर रहा है। –

+0

हां, यह एक अच्छी सुविधा है जिसे मैं पुराने फैशन # टेम्पलेट टेबल की तुलना में दक्षता के बारे में निश्चित नहीं था। इस पंक्तियों पर कुछ भी करने में कुछ समय लगता है। मेरे पास क्लस्टर्ड इंडेक्स है। यदि अधिक जरूरी है तो मैं निश्चित रूप से उन्हें जोड़ सकता हूं। –

1

... ऊपर ओएमजी पोनीज़ टिप्पणी के आधार पर, एक सीटीई विधि जो थोड़ा अधिक कॉम्पैक्ट है। यह विधि उन टेबलों पर अद्भुत काम करती है जहां आपके पास (किसी भी कारण से) कोई प्राथमिक कुंजी नहीं है - जहां आप पंक्तियां प्राप्त कर सकते हैं जो सभी कॉलम पर समान हैं।

;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

+0

यह बहुत कॉम्पैक्ट है, लेकिन मुझे गति में अधिक दिलचस्पी है। मैंने जो सीट्स के साथ पढ़ा और देखा है, उससे वे मेरे मामले में केवल वाक्य रचनात्मक चीनी हैं। अगर मैं गलत हूं, तो कृपया मुझे सही करें। –

+0

@ subt13: आपको विभिन्न विकल्पों के बीच वास्तविक क्वेरी योजना की तुलना करने के बाद हमें बताना होगा। –

संबंधित मुद्दे