2012-07-15 15 views
5

के साथ डुप्लिकेट हटाएं यहां डुप्लिकेट कॉलम के मान (Product) के साथ पंक्तियां हटाना चाहते हैं, जिसका उपयोग प्राथमिक कुंजी के रूप में किया जाएगा।कोई प्राथमिक कुंजी

कॉलम nvarchar का प्रकार है और हम एक उत्पाद के लिए 2 पंक्तियां नहीं चाहते हैं। डेटाबेस हजारों पंक्तियों के साथ एक बड़ा है हमें हटाने की आवश्यकता है।

सभी डुप्लिकेट के लिए क्वेरी के दौरान, हम पहले आइटम को रखना चाहते हैं और दूसरे को डुप्लिकेट के रूप में हटा देना चाहते हैं।

अभी तक कोई प्राथमिक कुंजी नहीं है, और हम इसे डुप्लीकेट हटाने की इस गतिविधि के बाद बनाना चाहते हैं। फिर Product कॉलम हमारी प्राथमिक कुंजी हो सकती है।

डेटाबेस SQL ​​सर्वर सीई है।

मैं कई तरीकों की कोशिश की, और ज्यादातर के समान त्रुटि मिल रही है:

क्वेरी पार्स करने में कोई त्रुटि थी। [टोकन लाइन नंबर = 2, टोकन लाइन ऑफसेट = 1, त्रुटि में टोकन = से]

एक तरीका है जिसके मैंने कोशिश की:

DELETE FROM TblProducts 
FROM TblProducts w 
    INNER JOIN (
      SELECT Product 
      FROM TblProducts 
      GROUP BY Product 
      HAVING COUNT(*) > 1 
      )Dup ON w.Product = Dup.Product 

पसंदीदा तरीका जानने के लिए और मेरे कोड को समायोजित करने की कोशिश कर रहा कुछ इसी तरह साथ (यह अभी तक सही नहीं है):

SELECT Product, COUNT(*) TotalCount 
FROM TblProducts 
GROUP BY Product 
HAVING COUNT(*) > 1 
ORDER BY COUNT(*) DESC 

-- 
;WITH cte -- These 3 lines are the lines I have more doubt on them 
    AS (SELECT ROW_NUMBER() OVER (PARTITION BY Product 
             ORDER BY (SELECT 0)) RN 
     FROM Word) 
DELETE FROM cte 
WHERE RN > 1 
+0

डेटाबेस कितना बड़ा है। क्या हम यहां लाखों पंक्तियों से बात कर रहे हैं? अरबों? –

+0

3000 डुप्लीकेट के साथ लगभग 200,000 रिकॉर्ड, इतना नहीं: डी – Sypress

+0

जब आपके पास उत्पाद के लिए एक ही डेटा के साथ दो रिकॉर्ड हैं, लेकिन अन्य कॉलम में अलग-अलग डेटा, आप कैसे जानते हैं कि रखने के लिए कौन सा सही है? –

उत्तर

4

यदि आपके पास एक ही उत्पाद कॉलम के साथ दो अलग-अलग रिकॉर्ड हैं, तो आप कुछ मानदंडों के साथ अवांछित रिकॉर्ड चुन सकते हैं, उदा।

CREATE TABLE victims AS 
    SELECT MAX(entryDate) AS date, Product, COUNT(*) AS dups FROM ProductsTable WHERE ... 
    GROUP BY Product HAVING dups > 1; 

फिर आप उत्पाद योग्य और पीड़ितों के बीच एक विलम्ब जॉइन कर सकते हैं।

या आप केवल उत्पाद का चयन कर सकते हैं, और फिर किसी अन्य जॉइन स्थिति के लिए एक डिलीट कर सकते हैं, उदाहरण के लिए एक अवैध ग्राहक आईडी, या EntryDate NULL, या कुछ और। यह काम करता है यदि आप जानते हैं कि उत्पाद की एक और केवल एक वैध प्रति है, और अन्य सभी अमान्य डेटा द्वारा पहचानने योग्य हैं।

मान लीजिए कि इसके बजाय आपके पास पहचानिक रिकॉर्ड हैं (या आपके पास समान और गैर-समान दोनों हैं, या आपके पास कुछ उत्पाद के लिए कई डुप्ली हो सकते हैं और आप नहीं जानते हैं)। आप बिल्कुल वही क्वेरी चलाते हैं। फिर, आप ProductsTable पर एक SELECT क्वेरी चलाते हैं और उत्पाद कोड से मेल खाने वाले सभी उत्पादों को उत्पादित करते हैं, उत्पाद द्वारा समूहित करते हैं, और सभी फ़ील्ड के लिए उपयुक्त समेकित फ़ंक्शन चुनते हैं (यदि समान है, तो कोई भी कुल करना चाहिए। अन्यथा मैं आमतौर पर MAX की कोशिश करता हूं या मिन)।यह प्रत्येक उत्पाद के लिए बिल्कुल एक पंक्ति "सहेज" देगा।

उस बिंदु पर आप डेली जॉइन चलाते हैं और सभी डुप्लिकेट उत्पादों को मार देते हैं। फिर, मुख्य तालिका में सहेजे गए और deduped सबसेट को बस पुनः आयात करें।

बेशक, डेली जॉइन और इंसर्ट चयन के बीच, आपके पास एक अस्थिर स्थिति में डीबी होगा, कम से कम एक डुप्लिकेट वाले सभी उत्पादों को आसानी से गायब कर दिया जाएगा।

एक और तरीका है जो MySQL में काम करना चाहिए:

-- Create an empty table 
CREATE TABLE deduped AS SELECT * FROM ProductsTable WHERE false; 

CREATE UNIQUE INDEX deduped_ndx ON deduped(Product); 

-- DROP duplicate rows, Joe the Butcher's way 
INSERT IGNORE INTO deduped SELECT * FROM ProductsTable; 

ALTER TABLE ProductsTable RENAME TO ProductsBackup; 

ALTER TABLE deduped RENAME TO ProductsTable; 
-- TODO: Copy all indexes from ProductsTable on deduped. 

नोट: ऊपर जिस तरह से काम नहीं करता है तो आप "अच्छा रिकॉर्ड" और "अवैध डुप्लिकेट" भेद करने के लिए चाहते हैं। यह केवल तभी काम करता है जब आपके पास अनावश्यक डुप्लिकेट रिकॉर्ड हैं, या यदि आपको परवाह नहीं है, तो पंक्ति जो आप रखती है और जिसे आप फेंक देते हैं!

EDIT: आप कहते हैं कि "डुप्लिकेट" में अवैध फ़ील्ड हैं। तो अगर आप उत्पाद के लिए केवल एक पंक्ति है, सब कुछ ठीक

SELECT * FROM ProductsTable ORDER BY Product, FieldWhichShouldNotBeNULL IS NULL; 

और अच्छा है, यह चयनित हो जाएगी: उस मामले में आप एक छंटाई चाल के साथ ऊपर संशोधित कर सकते हैं। यदि आपके पास और अधिक है, तो जिसके लिए (फ़ील्डविच शॉल्डनवेनबुल आईएस न्यूल) गलत है (यानी वह क्षेत्र जहां फ़ील्डविचशल्डनवर बीननल वास्तव में शून्य नहीं है) इसे पहले चुना जाएगा, और डाला जाएगा। अन्य सभी उत्पाद की विशिष्टता के खिलाफ, आईजीएनओआर क्लॉज के कारण चुपचाप उछालेंगे। ऐसा करने का वास्तव में एक सुंदर तरीका नहीं है (और जांच करें कि मैंने अपने खंड में झूठ के साथ सच नहीं मिला!), लेकिन इसे काम करना चाहिए।

संपादित
वास्तव में एक नया उत्तर

के अधिक यह समस्या को वर्णन करने

CREATE TABLE ProductTable (Product varchar(10), Description varchar(10)); 
INSERT INTO ProductTable VALUES ('CBPD10', 'C-Beam Prj'); 
INSERT INTO ProductTable VALUES ('CBPD11', 'C Proj Mk2'); 
INSERT INTO ProductTable VALUES ('CBPD12', 'C Proj Mk3'); 

कोई सूचकांक अभी तक है, और कोई प्राथमिक कुंजी है एक साधारण तालिका है। हम अभी भी प्राथमिक कुंजी होने के लिए उत्पाद घोषित कर सकते हैं।

लेकिन कुछ बुरा होता है। दो नए रिकॉर्ड मिलते हैं, और दोनों के पास पूर्ण विवरण है।

फिर भी, दूसरा एक मान्य उत्पाद है क्योंकि हम अब पहले सीबीपीडी 14 के बारे में कुछ नहीं जानते थे, और इसलिए हम इस रिकॉर्ड को पूरी तरह खोना नहीं चाहते हैं। हम करते हैं हालांकि नकली सीबीपीडी 10 से छुटकारा पाना चाहते हैं।

INSERT INTO ProductTable VALUES ('CBPD10', NULL); 
INSERT INTO ProductTable VALUES ('CBPD14', NULL); 

एक अशिष्ट ProductTable से हटाएँ कहां विवरण शून्य है सवाल से बाहर है, यह CBPD14 मार डालेंगे जो एक नकली नहीं है।

तो हम इसे ऐसा करते हैं।

SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1; 

हम मान लेते हैं कि:: सबसे पहले डुप्लिकेट की सूची प्राप्त "बुरा रिकॉर्ड के प्रत्येक सेट के लिए कम से कम एक अच्छा रिकॉर्ड नहीं है"।

हम इसके विपरीत और इसके लिए पूछताछ करके इस धारणा की जांच करते हैं। यदि सभी को copacetic है, तो हम इस क्वेरी को कुछ भी वापस करने की उम्मीद नहीं करते हैं।

SELECT Dups.Product FROM ProductTable 
RIGHT JOIN (SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1) AS Dups 
ON (ProductTable.Product = Dups.Product 
     AND ProductTable.Description IS NOT NULL) 
WHERE ProductTable.Description IS NULL; 

आगे सत्यापित करने के लिए, मैं दो रिकॉर्ड डालता हूं जो विफलता के इस मोड का प्रतिनिधित्व करते हैं; अब मैं उपरोक्त क्वेरी को नया कोड वापस करने की अपेक्षा करता हूं।

INSERT INTO ProductTable VALUES ("AC5", NULL), ("AC5", NULL); 

अब "जाँच" क्वेरी वास्तव में देता है,

AC5 

तो, dups की पीढ़ी अच्छा लगता है।

अब मैं सभी डुप्लिकेट रिकॉर्ड्स को हटाने के लिए आगे बढ़ता हूं जो मान्य नहीं हैं। यदि डुप्लिकेट, वैध रिकॉर्ड हैं, तो वे तब तक डुप्लीकेट रहेंगे जब तक कि कुछ शर्त नहीं मिलती, उनमें से एक "अच्छा" रिकॉर्ड और अन्य सभी को "अमान्य" घोषित करना (शायद विवरण से अलग क्षेत्र के साथ प्रक्रिया को दोहराएं)।

लेकिन अरे, एक रगड़ है। वर्तमान में, आप किसी तालिका से हटा नहीं सकते हैं और एक ही तालिका से उपकुंजी (http://dev.mysql.com/doc/refman/5.0/en/delete.html) में चयन कर सकते हैं। तो थोड़ा कामकाज की आवश्यकता है:

CREATE TEMPORARY TABLE Dups AS 
    SELECT Product, COUNT(*) AS Duplicates 
     FROM ProductTable GROUP BY Product HAVING Duplicates > 1; 

DELETE ProductTable FROM ProductTable JOIN Dups USING (Product) 
    WHERE Description IS NULL; 

अब यह सभी अमान्य रिकॉर्ड हटा देगा, बशर्ते वे डुप्स तालिका में दिखाई दें।

इसलिए हमारे सीबीपीडी 14 रिकॉर्ड को छूटा नहीं जाएगा, क्योंकि यह वहां दिखाई नहीं देता है। सीबीपीडी 10 के लिए "अच्छा" रिकॉर्ड छूटा नहीं जाएगा क्योंकि यह सच नहीं है कि इसका विवरण पूर्ण है। अन्य सभी - Poof।

मुझे राज्य फिर से है कि यह डुप्लिकेट है अगर एक रिकार्ड कोई वैध रिकॉर्ड और अभी तकहै, तो सभी प्रतियों कि रिकॉर्ड के मारे जाने देंगे - कोई जीवित बचे लोगों हो जाएगा।

इससे बचने के लिए पहले किसी अन्य टेम्पलेटरी तालिका में विफलता के इस मोड का प्रतिनिधित्व करने वाली पंक्तियों को ऊपर (ऊपर दिए गए प्रश्न का उपयोग करके, चेक "का उपयोग कर सकते हैं), फिर हटाए जाने के बाद उन्हें मुख्य तालिका में वापस ले जाएं (लेनदेन का उपयोग क्रम में हो सकता है)।

+0

जल्द ही प्रतिक्रिया देगी और धन्यवाद, धन्यवाद – Sypress

+0

मित्र, यदि संभव हो तो मैं आपके दृष्टिकोण के आधार पर प्रयास कर रहा हूं, कृपया कोड के नमूने 3-5 लाइनों को प्रदान करें सोचा और संक्षेप में। इसकी सराहना की जाएगी। – Sypress

+1

कर सकते हैं। मैं वास्तव में सुनिश्चित करने के लिए एक छोटा सा उदाहरण शामिल करूंगा कि मैं आपकी समस्या को समझता हूं। बड़ी मात्रा में डेटा को हटाने से मुझे हमेशा परेशान होता है :-) – LSerni

-2

इस प्रयास करें:

DELETE FROM TblProducts  
WHERE Product IN 
     (
    SELECT Product 
    FROM TblProducts 
    GROUP BY Product 
    HAVING COUNT(*) > 1) 

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

+0

सलाह के लिए धन्यवाद और जल्द ही इस पर एक प्रतिक्रिया दें, धन्यवाद – Sypress

+0

यह निष्पादन वास्तव में धीमा है!, यह लगभग आधे घंटे है ... – Sypress

+2

वाल्टर, यदि आप जानते हैं कि यह तालिका में प्रत्येक उत्पाद को ज़ैप करने जा रहा है तो आप कोड भी पोस्ट क्यों करेंगे एक डुप्लिकेट है (जिसमें से एक को रखने की जरूरत है)? उम्मीद है कि निष्पादित करने से पहले या तो साइप्रस कोड के नीचे पैराग्राफ पढ़ता है या हाल ही में पूर्ण बैकअप है ... – brian

1

पुरानी एक को पटकथा करके और इसे नामित करके एक नई तालिका बनाएं। पुराने टेबल से नए ऑब्जेक्ट्स (इंडेक्स इत्यादि) को भी स्क्रिप्ट करें। रखवाले को नई टेबल में डालें। यदि आप डेटाबेस हैं थोक-लॉग या सरल वसूली मॉडल में हैं, तो यह ऑपरेशन न्यूनतम रूप से लॉग किया जाएगा। पुरानी तालिका को छोड़ दें और फिर नए नाम को पुराने नाम पर पुनर्नामित करें।

हटाएं पर इस का लाभ यह होगा कि डालने न्यूनतम लॉग इन किया जा सकता है। हटाएं डबल काम करते हैं क्योंकि न केवल डेटा हटा दिया जाता है, लेकिन डिलीवरी लेनदेन लॉग में लिखा जाना चाहिए। बड़ी तालिकाओं के लिए, कम से कम लॉग इन आवेषण हटने से कहीं अधिक तेज होगा।

1

यदि यह है कि बड़ा नहीं है और आप कुछ अन्तराल है, और आप Sql सर्वर प्रबंधन स्टूडियो है, तो आप GUI का उपयोग कर मेज पर एक पहचान क्षेत्र डाल सकते हैं। अब आप अपने CTE की तरह स्थिति है सिवाय पंक्तियों खुद को सही मायने में अलग हैं। तो अब आप निम्नलिखित

SELECT MIN(table_a.MyTempIDField) 
FROM 
table_a lhs 
join table_1 rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
table_a.MyTempIDField <> table_b.MyTempIDField 
GROUP BY 
lhs.field1, rhs.field2 etc 

यह आपको सभी 'अच्छे' डुप्लिकेट दे सकता है। अब आप इस क्वेरी को डिलीट से क्वेरी से लपेट सकते हैं।

DELETE FROM lhs 
FROM table_a lhs 
join table_b rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
lhs.MyTempIDField <> rhs.MyTempIDField 
and lhs.MyTempIDField not in (

SELECT MIN(lhs.MyTempIDField) 
FROM 
table_a lhs 
join table_a rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
lhs.MyTempIDField <> rhs.MyTempIDField 
GROUP BY 
    lhs.field1, lhs.field2 etc 
) 
+0

हाय और धन्यवाद यह कोशिश करेंगे, क्या आपने माना है कि यह कॉम्पैक्ट संस्करण है? – Sypress

+0

भाषा के संदर्भ में कोई फर्क नहीं पड़ता, यदि आपको आवश्यकता हो तो स्क्रिप्ट के माध्यम से एक पहचान पंक्ति बहुत आसान जोड़ना चाहिए। –

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