Ich muss einer vorhandenen Tabelle eine eindeutige Einschränkung hinzufügen. Dies ist in Ordnung, außer dass die Tabelle bereits Millionen von Zeilen enthält und viele der Zeilen die eindeutige Einschränkung verletzen, die ich hinzufügen muss.
Was ist der schnellste Ansatz zum Entfernen der fehlerhaften Zeilen? Ich habe eine SQL-Anweisung, die die Duplikate findet und löscht, aber die Ausführung dauert ewig. Gibt es einen anderen Weg, um dieses Problem zu lösen? Vielleicht die Tabelle sichern und dann wiederherstellen, nachdem die Einschränkung hinzugefügt wurde?
CREATE TABLE tmp AS SELECT ...;
. Dann müssen Sie nicht einmal herausfinden, wie das Layouttmp
ist. :)Einige dieser Ansätze scheinen etwas kompliziert zu sein, und ich mache dies im Allgemeinen wie folgt:
Wenn die angegebene Tabelle
table
eindeutig ist (Feld1, Feld2), wobei die Zeile mit dem maximalen Feld3 beibehalten wird:Zum Beispiel habe ich eine Tabelle,
user_accounts
und möchte eine eindeutige Einschränkung für E-Mails hinzufügen, habe jedoch einige Duplikate. Sagen Sie auch, dass ich die zuletzt erstellte behalten möchte (maximale ID unter Duplikaten).USING
ist kein Standard-SQL, sondern eine PostgreSQL-Erweiterung (aber eine sehr nützliche), aber in der ursprünglichen Frage wird PostgreSQL ausdrücklich erwähnt.quelle
USING
in postgresql macht?WHERE table1.ctid<table2.ctid
- keine Notwendigkeit, serielle Spalte hinzuzufügenAnstatt eine neue Tabelle zu erstellen, können Sie auch eindeutige Zeilen nach dem Abschneiden wieder in dieselbe Tabelle einfügen. Machen Sie alles in einer Transaktion . Optional können Sie die temporäre Tabelle am Ende der Transaktion automatisch mit löschen
ON COMMIT DROP
. Siehe unten.Dieser Ansatz ist nur nützlich, wenn in der gesamten Tabelle viele Zeilen gelöscht werden müssen. Verwenden Sie für nur wenige Duplikate eine Ebene
DELETE
.Sie haben Millionen von Zeilen erwähnt. Um den Vorgang zu beschleunigen , möchten Sie genügend temporäre Puffer für die Sitzung zuweisen . Die Einstellung muss angepasst werden, bevor in Ihrer aktuellen Sitzung ein temporärer Puffer verwendet wird. Finden Sie die Größe Ihres Tisches heraus:
temp_buffers
Entsprechend einstellen . Großzügig aufrunden, da die In-Memory-Darstellung etwas mehr RAM benötigt.Diese Methode kann dem Erstellen einer neuen Tabelle überlegen sein, wenn abhängige Objekte vorhanden sind. Ansichten, Indizes, Fremdschlüssel oder andere Objekte, die auf die Tabelle verweisen.
TRUNCATE
Sie beginnen ohnehin mit einer sauberen Tafel (neue Datei im Hintergrund) und sind viel schneller alsDELETE FROM tbl
bei großen Tabellen (DELETE
können bei kleinen Tabellen sogar schneller sein).Bei großen Tabellen ist es regelmäßig schneller , Indizes und Fremdschlüssel zu löschen, die Tabelle neu zu füllen und diese Objekte neu zu erstellen. In Bezug auf fk-Einschränkungen müssen Sie natürlich sicher sein, dass die neuen Daten gültig sind. Andernfalls tritt beim Versuch, fk zu erstellen, eine Ausnahme auf.
Beachten Sie, dass
TRUNCATE
eine aggressivere Verriegelung erforderlich ist alsDELETE
. Dies kann ein Problem für Tabellen mit hoher gleichzeitiger Belastung sein.Wenn dies
TRUNCATE
keine Option ist oder generell für kleine bis mittlere Tabellen gilt, gibt es eine ähnliche Technik mit einem datenmodifizierenden CTE (Postgres 9.1 +):Langsamer für große Tische, weil
TRUNCATE
es dort schneller ist. Kann aber für kleine Tische schneller (und einfacher!) Sein.Wenn Sie überhaupt keine abhängigen Objekte haben, können Sie eine neue Tabelle erstellen und die alte löschen, aber Sie gewinnen kaum etwas über diesen universellen Ansatz.
Bei sehr großen Tabellen, die nicht in den verfügbaren Arbeitsspeicher passen , ist das Erstellen einer neuen Tabelle erheblich schneller. Sie müssen dies gegen mögliche Probleme / Overhead mit abhängigen Objekten abwägen.
quelle
TRUNCATE
. Stellen Sie, wie Erwin sagte, sicher, dass es vorhanden ist, bevor Sie Ihre Tabelle abschneiden. Siehe @ Codebykats AntwortON COMMIT DROP
, damit Leute, die den Teil verpassen, in dem ich "in einer Transaktion" geschrieben habe, keine Daten verlieren. Und ich habe BEGIN / COMMIT hinzugefügt, um "eine Transaktion" zu verdeutlichen.Sie können oid oder ctid verwenden, bei denen es sich normalerweise um "nicht sichtbare" Spalten in der Tabelle handelt:
quelle
NOT EXISTS
sollte erheblich schneller sein :DELETE FROM tbl t WHERE EXISTS (SELECT 1 FROM tbl t1 WHERE t1.dist_col = t.dist_col AND t1.ctid > t.ctid)
- oder verwenden Sie eine andere Spalte oder einen anderen Satz von Spalten zum Sortieren, um einen Überlebenden auszuwählen.NOT EXISTS
?EXISTS
hier sein. Lesen Sie es so: "Löschen Sie alle Zeilen, in denen eine andere Zeile mit demselben Wert indist_col
einer größeren vorhanden istctid
". Der einzige Überlebende pro Gruppe von Betrügern wird der mit dem größten seinctid
.LIMIT
wenn Sie die Anzahl der Duplikate kennen.Die PostgreSQL-Fensterfunktion ist für dieses Problem praktisch.
Siehe Löschen von Duplikaten .
quelle
Aus einer alten Mailingliste von postgresql.org :
Einzigartige Werte
Doppelte Werte
Noch ein doppeltes Duplikat
Wählen Sie doppelte Zeilen aus
Doppelte Zeilen löschen
Hinweis: PostgreSQL unterstützt keine Aliase für die in der
from
Klausel eines Löschvorgangs erwähnte Tabelle .quelle
Verallgemeinerte Abfrage zum Löschen von Duplikaten:
Die Spalte
ctid
ist eine spezielle Spalte, die für jede Tabelle verfügbar ist, jedoch nur sichtbar ist, wenn dies ausdrücklich erwähnt wird. Derctid
Spaltenwert wird für jede Zeile in einer Tabelle als eindeutig betrachtet.quelle
GROUP BY
Klausel korrekt anzugeben - dies sollte das "Eindeutigkeitskriterium" sein, gegen das jetzt verstoßen wird, oder wenn der Schlüssel Duplikate erkennen soll. Wenn falsch angegeben, funktioniert es nicht richtigIch habe gerade Erwin Brandstetters Antwort erfolgreich verwendet, um Duplikate in einer Join-Tabelle zu entfernen (eine Tabelle ohne eigene primäre IDs), aber festgestellt, dass es eine wichtige Einschränkung gibt.
Einschließen
ON COMMIT DROP
bedeutet, dass die temporäre Tabelle am Ende der Transaktion gelöscht wird. Für mich bedeutete dies, dass die temporäre Tabelle zum Zeitpunkt des Einfügens nicht mehr verfügbar war !Ich habe es gerade getan
CREATE TEMPORARY TABLE t_tmp AS SELECT DISTINCT * FROM tbl;
und alles hat gut funktioniert.Die temporäre Tabelle wird am Ende der Sitzung gelöscht.
quelle
Diese Funktion entfernt Duplikate, ohne Indizes zu entfernen, und führt sie für jede Tabelle aus.
Verwendung:
select remove_duplicates('mytable');
quelle
quelle
Wenn Sie nur einen oder mehrere doppelte Einträge haben und diese tatsächlich doppelt vorhanden sind (dh zweimal angezeigt werden), können Sie
ctid
die oben vorgeschlagene Spalte "versteckt" zusammen mitLIMIT
:Dadurch wird nur die erste der ausgewählten Zeilen gelöscht.
quelle
Zunächst müssen Sie entscheiden, welche Ihrer "Duplikate" Sie behalten möchten. Wenn alle Spalten gleich sind, können Sie jede von ihnen löschen ... Aber vielleicht möchten Sie nur das aktuellste oder ein anderes Kriterium beibehalten?
Der schnellste Weg hängt von Ihrer Antwort auf die obige Frage und auch vom Prozentsatz der Duplikate in der Tabelle ab. Wenn Sie 50% Ihrer Zeilen wegwerfen, ist es besser, dies zu tun
CREATE TABLE ... AS SELECT DISTINCT ... FROM ... ;
, wenn Sie 1% der Zeilen löschen, ist die Verwendung von LÖSCHEN besser.Auch für
work_mem
solche Wartungsvorgänge ist es im Allgemeinen gut, einen guten Teil Ihres Arbeitsspeichers festzulegen: Führen Sie EXPLAIN aus, überprüfen Sie die Anzahl N von Sortierungen / Hashes und setzen Sie work_mem auf Ihren Arbeitsspeicher / 2 / N. Verwenden Sie viel Arbeitsspeicher. Es ist gut für die Geschwindigkeit. Solange Sie nur eine gleichzeitige Verbindung haben ...quelle
Ich arbeite mit PostgreSQL 8.4. Als ich den vorgeschlagenen Code ausführte, stellte ich fest, dass die Duplikate nicht tatsächlich entfernt wurden. Bei einigen Tests habe ich festgestellt, dass das Hinzufügen von "DISTINCT ON (duplicate_column_name)" und "ORDER BY duplicate_column_name" den Trick getan hat. Ich bin kein SQL-Guru, ich habe dies im PostgreSQL 8.4 SELECT ... DISTINCT-Dokument gefunden.
quelle
Das funktioniert sehr gut und geht sehr schnell:
quelle
Löschen Sie Duplikate nach Spalte (n) und behalten Sie die Zeile mit der niedrigsten ID bei. Das Muster stammt aus dem Postgres-Wiki
Mit CTEs können Sie dadurch eine besser lesbare Version der oben genannten erreichen
quelle
quelle