PostgreSQL में डुप्लिकेट रिकॉर्ड हटाएं


113

मेरे पास PostgreSQL 8.3.8 डेटाबेस में एक मेज है, जिस पर कोई कुंजी / बाधा नहीं है, और बिल्कुल समान मान वाली कई पंक्तियाँ हैं।

मैं सभी डुप्लिकेट को निकालना चाहूंगा और प्रत्येक पंक्ति की केवल 1 प्रति रखूंगा।

विशेष रूप से ("कुंजी" नाम दिया गया) में एक कॉलम है जिसका उपयोग डुप्लिकेट की पहचान करने के लिए किया जा सकता है (यानी प्रत्येक विशिष्ट "कुंजी" के लिए केवल एक प्रविष्टि मौजूद होनी चाहिए)।

मैं यह कैसे कर सकता हूँ? (आदर्श रूप से एकल एसक्यूएल कमांड के साथ) स्पीड इस मामले में कोई समस्या नहीं है (केवल कुछ पंक्तियाँ हैं)।

जवाबों:


80
DELETE FROM dupes a
WHERE a.ctid <> (SELECT min(b.ctid)
                 FROM   dupes b
                 WHERE  a.key = b.key);

20
इसका उपयोग न करें, यह बहुत धीमा है!
पावेल मालिसक

5
हालांकि यह समाधान निश्चित रूप से काम करता है, नीचे @rapimo का समाधान बहुत तेजी से निष्पादित होता है। मेरा मानना ​​है कि यह आंतरिक चयन कथन के साथ करना है, जो कि दूसरे समाधान में चल रहे समूह के बजाय एन बार (सभी तालिका में एन पंक्तियों के लिए) निष्पादित किया जा रहा है।
डेविड

विशाल तालिकाओं (कई मिलियन रिकॉर्ड) के लिए, यह वास्तव में @ रैपिमो के समाधान के विपरीत, स्मृति में फिट बैठता है। तो उन मामलों में यह सबसे तेज है (कोई स्वैपिंग नहीं)।
गेल

1
स्पष्टीकरण जोड़ना: यह काम करता है क्योंकि ctid एक विशेष पोस्टग्रेज कॉलम है जो पंक्ति के भौतिक स्थान को दर्शाता है। आप इसे यूनिक आईडी के रूप में उपयोग कर सकते हैं, भले ही आपकी टेबल में यूनिक आईडी न हो। postgresql.org/docs/8.2/ddl-system-columns.html
एरिक ब्यूरल

194

एक तेज समाधान है

DELETE FROM dups a USING (
      SELECT MIN(ctid) as ctid, key
        FROM dups 
        GROUP BY key HAVING COUNT(*) > 1
      ) b
      WHERE a.key = b.key 
      AND a.ctid <> b.ctid

20
यह a_horse_with_no_name के समाधान से अधिक तेज़ क्यों है?
रॉबर्टो

3
यह तेज़ है क्योंकि यह केवल 2 क्वेरी चलाता है। पहले सभी डुप्लिकेट का चयन करने के लिए, फिर तालिका से सभी आइटम हटाने के लिए। @A_horse_with_no_name द्वारा क्वेरी यह देखने के लिए एक क्वेरी करता है कि क्या यह तालिका में हर एक आइटम के लिए किसी अन्य से मेल खाता है।
ऐयोलून

5
क्या है ctid?
techkuz

6
डॉक्स से: ctid अपनी तालिका के भीतर पंक्ति संस्करण का भौतिक स्थान। ध्यान दें कि हालांकि ctid का उपयोग पंक्ति संस्करण को बहुत जल्दी पता लगाने के लिए किया जा सकता है, एक पंक्ति के ctid को हर बार अपडेट होने या VACUUM FULL द्वारा ले जाने पर बदल जाएगा। इसलिए ctid एक लंबी अवधि की पंक्ति पहचानकर्ता के रूप में बेकार है।
Saim

1
ऐसा लगता है कि 2 से अधिक डुप्लिकेट पंक्तियाँ होने पर काम नहीं करता है, क्योंकि यह समय पर केवल एक डुप्लिकेट को हटाता है।
फ्रेंकी ड्रेक

74

यह तेज और संक्षिप्त है:

DELETE FROM dupes T1
    USING   dupes T2
WHERE   T1.ctid < T2.ctid  -- delete the older versions
    AND T1.key  = T2.key;  -- add more columns if needed

अद्वितीय पहचानकर्ता के बिना डुप्लिकेट पंक्तियों को हटाने के लिए मेरा जवाब भी देखें जिसमें अधिक जानकारी शामिल है।


सीटी क्या है? गिनती?
Techkuz

4
@trthhrtz ctidतालिका में रिकॉर्ड के भौतिक स्थान को इंगित करता है। टिप्पणी में उस समय जो मैंने लिखा था, उसके विपरीत, ऑपरेटर से कम का उपयोग करना पुराने संस्करण को इंगित करने के लिए जरूरी नहीं है क्योंकि सीटी चारों ओर लपेट सकता है और कम सीटीटी के साथ एक मूल्य वास्तव में नया हो सकता है।
isapir

1
सिर्फ FYI करें, मैंने इस समाधान की कोशिश की, और 15 मिनट इंतजार करने के बाद इसे समाप्त कर दिया। रैपिमो के समाधान की कोशिश की और यह लगभग 10 सेकंड (~ 700,000 पंक्तियों को हटा दिया गया) में पूरा हुआ।
पैट्रिक

@ पैट्रिक कल्पना नहीं कर सकता है कि आपके डीबी के पास एक विशिष्ट पहचानकर्ता नहीं है क्योंकि रैपिमो का जवाब उस मामले में काम नहीं करता है।
स्टकैश

@ आईसपिर मैं बस उत्सुक हूं, ऊपर दिए गए उत्तर, वे पुराने रिकॉर्ड्स को सही रख रहे हैं जैसे उन्होंने चुना था min(ctid)? जबकि तुम्हारा नया रख रहे हैं? धन्यवाद!
स्टकैश

17

मैंने यह कोशिश की:

DELETE FROM tablename
WHERE id IN (SELECT id
              FROM (SELECT id,
                             ROW_NUMBER() OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum
                     FROM tablename) t
              WHERE t.rnum > 1);

Postgres wiki द्वारा प्रदान किया गया:

https://wiki.postgresql.org/wiki/Deleting_duplicates


@ रैपिमो के जवाब और स्वीकृत एक (@a_horse_with_no_name) की तुलना में प्रदर्शन का कोई विचार?
tuxayo

3
यह काम नहीं करेगा, जैसे कि प्रश्न बताता है, सभी कॉलम समान हैं, idशामिल हैं।
ibizaman

यह क्वेरी मूल प्रतिलिपि और डुप्लिकेट दोनों को हटा देगी। सवाल कम से कम एक पंक्ति को बनाए रखने के बारे में है।
pyBomb

@pyBomb गलत है, यह पहला idस्थान रखेगा जहां कॉलम 1 ... 3 डुप्लिकेट हैं
जेफ

Postgresql 12 के रूप में, यह BY FAR सबसे तेज समाधान (300 मिलियन पंक्तियों के खिलाफ) है। मैंने इस प्रश्न में प्रस्तावित सब कुछ का परीक्षण किया, जिसमें स्वीकृत उत्तर भी शामिल है, और यह "आधिकारिक" समाधान वास्तव में सबसे तेज़ है और ओपी (और मेरा) से सभी आवश्यकताओं को पूरा करता है
जेफ

7

मैं एक अस्थायी तालिका का उपयोग करूंगा:

create table tab_temp as
select distinct f1, f2, f3, fn
  from tab;

फिर, हटाना tabऔर का नाम बदलने tab_tempमें tab


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

1
जिसकी जरूरत हर किसी को नहीं होती। यह दृष्टिकोण बहुत तेज़ है और इंडेक्स के बिना 200k ईमेल (varchar 250) पर बाकी की तुलना में बहुत बेहतर काम करता है।
सर्गेई टेलशेव्स्की

1
पूर्ण कोड:DROP TABLE IF EXISTS tmp; CREATE TABLE tmp as ( SELECT * from (SELECT DISTINCT * FROM your_table) as t ); DELETE from your_table; INSERT INTO your_table SELECT * from tmp; DROP TABLE tmp;
एरिक बूरे

7

मुझे अपना खुद का संस्करण बनाना था। @A_horse_with_no_name द्वारा लिखित संस्करण मेरी तालिका (21M पंक्तियों) पर बहुत धीमा है। और @rapimo केवल डंप को नष्ट नहीं करता है।

यहाँ मैं PostgreSQL 9.5 पर उपयोग क्या है

DELETE FROM your_table
WHERE ctid IN (
  SELECT unnest(array_remove(all_ctids, actid))
  FROM (
         SELECT
           min(b.ctid)     AS actid,
           array_agg(ctid) AS all_ctids
         FROM your_table b
         GROUP BY key1, key2, key3, key4
         HAVING count(*) > 1) c);

1

एक अन्य दृष्टिकोण (केवल तभी काम करता है जब आपके पास idअपनी तालिका में कोई विशिष्ट फ़ील्ड हो ) स्तंभों द्वारा सभी अद्वितीय आईडी खोजने और अन्य आईडी निकालने के लिए जो अद्वितीय सूची में नहीं हैं

DELETE
FROM users
WHERE users.id NOT IN (SELECT DISTINCT ON (username, email) id FROM users);

बात यह है, मेरे सवाल में तालिकाओं की कोई अनोखी आईडी नहीं थी; "डुप्लिकेट" सभी पंक्तियों पर समान मान वाले कई पंक्तियाँ थीं।
एंड्रे मोरुजा

ठीक है, मैंने कुछ नोट जोड़े
ज़ायतसेव दिमित्री

1

कैसा रहेगा:

साथ में
  यू के रूप में (DISTINCT का चयन करें)
  x AS (DELETE FROM your_table से)
INSERT में आपका_चुनना चुनें * आप से;

मैं निष्पादन आदेश के बारे में चिंतित था, क्या DELETE SELECT DISTINCT से पहले होगा, लेकिन यह मेरे लिए ठीक काम करता है। और तालिका संरचना के बारे में किसी भी ज्ञान की आवश्यकता नहीं होने का अतिरिक्त बोनस है।


एकमात्र दोष यह है, कि यदि आपके पास डेटा प्रकार है जो समानता का समर्थन नहीं करता (जैसे json) यह काम नहीं करेगा।
a_horse_with_no_name

0

इसने मेरे लिए अच्छा काम किया। मेरे पास एक तालिका थी, शर्तें, जिसमें डुप्लिकेट मान शामिल थे। सभी डुप्लिकेट पंक्तियों के साथ एक अस्थायी तालिका को पॉप्युलेट करने के लिए एक क्वेरी चलाएँ। फिर मैंने टेम्प टेबल में उन आईडी के साथ एक डिलीट स्टेटमेंट चलाया। मान वह कॉलम है जिसमें डुप्लिकेट सम्‍मिलित है।

        CREATE TEMP TABLE dupids AS
        select id from (
                    select value, id, row_number() 
over (partition by value order by value) 
    as rownum from terms
                  ) tmp
                  where rownum >= 2;

delete from [table] where id in (select id from dupids)

0

यहाँ एक समाधान का उपयोग कर रहा है PARTITION BY:

DELETE FROM dups
USING (
  SELECT
    ctid,
    (ctid != min(ctid) OVER (PARTITION BY key_column1, key_column2 [...])) AS is_duplicate
  FROM dups 
) dups_find_duplicates
WHERE dups.ctid == dups_find_duplicates.ctid
AND dups_find_duplicates.is_duplicate
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.