it-swarm.com.de

So löschen Sie doppelte Zeilen ohne eindeutige Kennung

Ich habe doppelte Zeilen in meiner Tabelle und möchte Duplikate möglichst effizient löschen, da die Tabelle groß ist. Nach einigen Nachforschungen bin ich zu dieser Frage gekommen:

WITH TempEmp AS
(
SELECT name, ROW_NUMBER() OVER(PARTITION by name, address, zipcode ORDER BY name) AS duplicateRecCount
FROM mytable
)
-- Now Delete Duplicate Records
DELETE FROM TempEmp
WHERE duplicateRecCount > 1;

Aber es funktioniert nur in SQL, nicht in Netezza. Es scheint, dass die DELETE nach der WITH-Klausel nicht gefällt?

34
moe

Ich mag die Lösung von @ erwin-brandstetter, wollte aber eine Lösung mit dem Schlüsselwort USING zeigen:

DELETE   FROM table_with_dups T1
  USING       table_with_dups T2
WHERE  T1.ctid    < T2.ctid       -- delete the "older" ones
  AND  T1.name    = T2.name       -- list columns that define duplicates
  AND  T1.address = T2.address
  AND  T1.zipcode = T2.zipcode;

Wenn Sie die Datensätze überprüfen möchten, bevor Sie sie löschen, ersetzen Sie einfach DELETE durch SELECT * und USING durch ein Komma ,, d. H.

SELECT * FROM table_with_dups T1
  ,           table_with_dups T2
WHERE  T1.ctid    < T2.ctid       -- select the "older" ones
  AND  T1.name    = T2.name       -- list columns that define duplicates
  AND  T1.address = T2.address
  AND  T1.zipcode = T2.zipcode;

Update: Ich habe hier einige der verschiedenen Lösungen auf Geschwindigkeit getestet. Wenn Sie nicht mit vielen Duplikaten rechnen, ist diese Lösung wesentlich besser als diejenigen mit einer NOT IN (...)-Klausel, da diese viele Zeilen in der Unterabfrage erzeugen.

Wenn Sie die Abfrage neu schreiben, um IN (...) zu verwenden, funktioniert sie ähnlich wie die hier vorgestellte Lösung, der SQL-Code wird jedoch weniger präzise.

Update 2: Wenn Sie NULL-Werte in einer der Schlüsselspalten haben (die Sie eigentlich nicht IMO sein sollten), können Sie COALESCE() in der Bedingung für diese Spalte verwenden, z.

  AND COALESCE(T1.col_with_nulls, '[NULL]') = COALESCE(T2.col_with_nulls, '[NULL]')
25
isapir

Wenn Sie keine andere eindeutige Kennung haben, können Sie ctid verwenden:

delete from mytable
    where exists (select 1
                  from mytable t2
                  where t2.name = mytable.name and
                        t2.address = mytable.address and
                        t2.Zip = mytable.Zip and
                        t2.ctid > mytable.ctid
                 );

Es ist eine gute Idee, eine eindeutige, automatisch inkrementierende ID in jeder Tabelle zu haben. Ein solches delete ist ein wichtiger Grund dafür.

48
Gordon Linoff

In einer perfekten Welt hat every eine eindeutige Kennung.
Wenn keine eindeutige Spalte (oder eine Kombination davon) vorhanden ist, verwenden Sie die ctid -Spalte :

DELETE FROM tbl
WHERE  ctid NOT IN (
   SELECT min(ctid)                    -- ctid is NOT NULL by definition
   FROM   tbl
   GROUP  BY name, address, zipcode);  -- list columns defining duplicates

Die obige Abfrage ist kurz und listet die Spaltennamen nur einmal auf. NOT IN (SELECT ...) ist ein kniffliger Abfragestil, wenn NULL-Werte beteiligt sein können, die Systemspalte ctid jedoch niemals NULL ist. Sehen:

Die Verwendung von EXISTS als demonstriert von @Gordon ist normalerweise schneller. Ist also ein Self-Join mit der USING -Klausel wie @isapir später hinzugefügt . Beide sollten zu demselben Abfrageplan führen.

Beachten Sie jedoch einen wichtigen Unterschied : Diese anderen Abfragen behandeln NULL Werte als ungleich, während GROUP BY (oder DISTINCT oder DISTINCT ON () ) behandelt NULL-Werte als gleich. Egal ob Schlüsselspalten definiert sind NOT NULL. Ansonsten benötigen Sie je nach Ihrer Definition von "duplizieren" den einen oder anderen Ansatz. Oder verwenden Sie IS NOT DISTINCT FROM zum Vergleich von Werten (die möglicherweise einige Indizes nicht verwenden können).

Haftungsausschluss:

ctid ist ein internes Implementierungsdetail von Postgres. Es ist nicht im SQL-Standard enthalten und kann ohne Vorwarnung zwischen Hauptversionen geändert werden (auch wenn dies sehr unwahrscheinlich ist). Die Werte können sich zwischen Befehlen aufgrund von Hintergrundprozessen oder gleichzeitigen Schreibvorgängen ändern (jedoch nicht innerhalb desselben Befehls).

Verbunden:

Beiseite:

Das Ziel einer DELETE -Anweisung kann nicht der CTE sein, sondern nur die zugrunde liegende Tabelle. Das ist ein Nebeneffekt von SQL Server - genau wie Ihr ganzer Ansatz.

21

Hier ist was ich mit einem group by gefunden habe

DELETE FROM mytable
WHERE id NOT in (
  SELECT MIN(id) 
  FROM mytable
  GROUP BY name, address, zipcode
)

Es löscht die Duplikate und behält den ältesten Datensatz bei, der Duplikate hat.

10
Bruno Calza

Wir können eine Fensterfunktion verwenden, um doppelte Zeilen sehr effektiv zu entfernen:

DELETE FROM tab 
  WHERE id IN (SELECT id 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), id 
                           FROM tab) x 
                 WHERE x.row_number > 1);

Einige optimierte PostgreSQL-Versionen (mit ctid):

DELETE FROM tab 
  WHERE ctid = ANY(ARRAY(SELECT ctid 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), ctid 
                           FROM tab) x 
                 WHERE x.row_number > 1));
6
Vivek S.

Die gültige Syntax ist unter http://www.postgresql.org/docs/current/static/sql-delete.html angegeben.

Ich würde Ihre Tabelle ALTERNIEREN, um eine eindeutige, automatisch inkrementierende Primärschlüssel-ID hinzuzufügen, sodass Sie eine Abfrage wie die folgende ausführen können, die das erste jedes Duplikatsatzes (dh die mit der niedrigsten ID) enthält. Beachten Sie, dass das Hinzufügen des Schlüssels in Postgres etwas komplizierter ist als in anderen DBs. 

DELETE FROM mytable d USING (
  SELECT min(id), name, address, Zip 
  FROM mytable 
  GROUP BY name, address, Zip HAVING COUNT() > 1
) AS k 
WHERE d.id <> k.id 
AND d.name=k.name 
AND d.address=k.address 
AND d.Zip=k.Zip;
2
Joe Murray

Wenn Sie eine Zeile aus doppelten Zeilen in der Tabelle heraushalten möchten.

create table some_name_for_new_table as 
(select * from (select *,row_number() over (partition by pk_id) row_n from 
your_table_name_where_duplicates_are_present) a where row_n = 1);

Dadurch wird eine Tabelle erstellt, die Sie kopieren können. 

Bevor Sie die Tabelle kopieren, löschen Sie bitte die Spalte 'row_n'. 

1

Wenn Sie für jede Zeile einen eindeutigen Bezeichner wünschen, können Sie einfach einen (seriellen oder guid) Namen hinzufügen und diesen als Ersatzschlüssel behandeln.


CREATE TABLE thenames
        ( name text not null
        , address text not null
        , zipcode text not null
        );
INSERT INTO thenames(name,address,zipcode) VALUES
('James', 'main street', '123' )
,('James', 'main street', '123' )
,('James', 'void street', '456')
,('Alice', 'union square' , '123')
        ;

SELECT*FROM thenames;

        -- add a surrogate key
ALTER TABLE thenames
        ADD COLUMN seq serial NOT NULL PRIMARY KEY
        ;
SELECT*FROM thenames;

DELETE FROM thenames del
WHERE EXISTS(
        SELECT*FROM thenames x
        WHERE x.name=del.name
        AND x.address=del.address
        AND x.zipcode=del.zipcode
        AND x.seq < del.seq
        );

        -- add the unique constrain,so that new dupplicates cannot be created in the future
ALTER TABLE thenames
        ADD UNIQUE (name,address,zipcode)
        ;

SELECT*FROM thenames;
0
wildplasser

Aus der Dokumentation doppelte Zeilen löschen

Eine häufige Frage in IRC ist das Löschen von Zeilen, die über eine Spaltengruppe dupliziert werden, wobei nur die mit der niedrigsten ID verwendet wird .. Diese Abfrage führt dies für alle Zeilen mit Tabellennamen mit derselben Spalte1, Spalte2 durch und Spalte3.

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

Manchmal wird ein Zeitstempelfeld anstelle eines ID-Felds verwendet.

0
Chad Crowe