2011-07-05 29 views
40

Mam tabelę w bazie danych PostgreSQL 8.3.8, która nie zawiera żadnych kluczy/ograniczeń i ma wiele wierszy z dokładnie tymi samymi wartościami.Usuwanie duplikatów rekordów w PostgreSQL

Chciałbym usunąć wszystkie duplikaty i zachować tylko 1 kopię każdego wiersza.

W szczególności istnieje jedna kolumna (zwana "kluczem"), która może służyć do identyfikacji duplikatów (tj. Powinien istnieć tylko jeden wpis dla każdego odrębnego "klucza").

Jak mogę to zrobić? (najlepiej z pojedynczym poleceniem SQL) Prędkość nie jest w tym przypadku problemem (jest tylko kilka wierszy).

Odpowiedz

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

Idealne, dzięki! Nie wiedziałem o ctid –

+4

Nie używaj go, jest za wolno! –

+2

Chociaż to rozwiązanie zdecydowanie działa, @rapimo [rozwiązanie poniżej] (https://stackoverflow.com/a/12963112/1156554) wykonuje znacznie szybciej. Uważam, że ma to związek z tym, że instrukcja wewnętrznego wyboru jest wykonywana N razy (dla wszystkich N wierszy w tabeli duplikatów) zamiast grupowania, które jest realizowane w innym rozwiązaniu. – David

4

chciałbym użyć tabeli tymczasowej:

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

Następnie usuń tab i zmienić tab_temp do tab.

+4

Takie podejście nie uwzględnia wyzwalaczy, indeksów i statystyk. Z pewnością możesz je dodać, ale dodaje też dużo więcej pracy. – Jordan

+0

Nie każdy tego potrzebuje. To podejście jest niezwykle szybkie i działało znacznie lepiej niż reszta na e-maile 200k (varchar 250) bez indeksów. –

58

Szybszy rozwiązaniem jest

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

Dlaczego jest szybszy niż rozwiązanie a_horse_with_no_name? – Roberto

+1

Zostało to wykonane w ciągu sekundy w teście, w porównaniu z 53 minutami dla zaakceptowanego rozwiązania. – Alex

+0

To naprawdę jest szybsze! – Alfabravo

5

musiałem stworzyć własną wersję. Wersja napisana przez @a_horse_with_no_name jest zbyt powolna na moim stole (21M wierszy). A @rapimo po prostu nie usuwa duplikatów.

Oto co używam na 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); 
11

Próbowałem to:

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

dostarczone przez Postgres wiki:

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

+0

Czy masz pojęcie o wydajności w porównaniu z odpowiedzią @ rapimo i zaakceptowaną (@a_horse_with_no_name)? – tuxayo

+1

Ten nie zadziała, jeśli, jak stwierdziły pytania, wszystkie kolumny są identyczne, włącznie z identyfikatorem "id". – ibizaman

0

To działa dobrze dla mnie. Miałem tabelę, warunki, które zawierały zduplikowane wartości. Wykonano kwerendę, aby wypełnić tabelę tymczasową wszystkimi zduplikowanymi wierszami. Następnie uruchomiłem instrukcję delete z tymi identyfikatorami w tabeli tymczasowej. wartość to kolumna zawierająca duplikaty.

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

działa zarówno NORMALNEGO SQL i PostgreSQL (RÓWNIEŻ PRACE NA AWS przesunięciu ku czerwieni)

DROP TABLE IF EXISTS backupOfTheTableContainingDuplicates; 

CREATE TABLE aNewEmptyTemporaryOrBackupTable 
AS SELECT DISTINCT * FROM originalTableContainingDuplicates; 

TRUNCATE TABLE originalTableContainingDuplicates; 

INSERT INTO originalTableContainingDuplicates SELECT * FROM 
aNewEmptyTemporaryOrBackupTable ; 

DROP TABLE aNewEmptyTemporaryOrBackupTable ; 

WYJAŚNIENIE z powyższych SQL SCRIPT

Więc

The 1st zapytanie zapewnia, jeśli posiadasz kopię zapasową/tabelę tymczasową oryginalnej tabeli zawierającej g duplikuje, a następnie upuść tę tabelę.

Druga kwerenda, tworzy nową tabelę (tymczasową/zapasową) z unikatowymi wpisami w oryginalnej tabeli zawierającej duplikat, więc nowa tabela tymczasowa jest taka sama jak oryginalna tabela MINUS duplikatów.

Trzecia kwerenda, obcina lub opróżnia oryginalną tabelę.

4. Zapytanie, wstawia lub kopiuje wszystkie unikatowe wpisy w tabeli tymczasowej do oryginalnej tabeli, która została ostatnio obcięta (nie ma danych). Po wykonaniu tej kwerendy, Tablica Oryginalna zostanie wypełniona danymi UNIQUE, które znajdowały się w tabeli tymczasowej.

Piąta kwerenda, usuwa/usuwa niepotrzebny tabelę tymczasową.

Wynik końcowy to, że oryginalna tabela ma tylko UNIKALNE ENTRIES i żadnych duplikatów.

5

Jest to szybki i zwięzły:

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

to jest doskonałe! – user151496

Powiązane problemy