Effizienteste Methode zum Löschen von Zeilen aus Postgres

23

Ich frage mich, wie man am effizientesten eine große Anzahl von Zeilen aus PostgreSQL löschen kann. Dieser Vorgang ist Teil einer täglichen wiederkehrenden Aufgabe, um Daten (ein Delta aus Einfügungen und Löschungen) in eine Tabelle zu importieren. Möglicherweise müssen Tausende, möglicherweise Millionen Zeilen gelöscht werden.

Ich habe eine Datei mit Primärschlüsseln, eine pro Zeile. Die beiden Optionen, über die ich nachgedacht habe, sind im folgenden Sinne, aber ich kenne / verstehe die Interna von PostgreSQL nicht genug, um eine fundierte Entscheidung zu treffen, die am besten wäre.

  • Führen Sie eine DELETEAbfrage für jede Zeile in der Datei mit einem einfachen WHEREPrimärschlüssel aus (oder gruppieren Sie die Löschvorgänge in Gruppen, nindem Sie eine IN()Klausel verwenden).
  • Importieren Sie die Primärschlüssel mithilfe des COPYBefehls in eine temporäre Tabelle und löschen Sie sie anschließend mithilfe eines Joins aus der Haupttabelle

Anregungen werden sehr geschätzt!

Tarnfeld
quelle
1
Dieselbe
Simon

Antworten:

25

Ihre zweite Option ist weitaus sauberer und wird gut genug abschneiden, damit es sich lohnt. Ihre Alternative besteht darin, gigantische Abfragen zu erstellen, deren Planung und Ausführung ziemlich mühsam sein wird. Im Allgemeinen ist es besser, PostgreSQL die Arbeit hier machen zu lassen. Im Allgemeinen habe ich Aktualisierungen in Zehntausenden von Zeilen in der von Ihnen beschriebenen Weise gefunden, um eine angemessene Leistung zu erzielen, aber es gibt eine wichtige Sache, die Sie vermeiden sollten.

Der Weg, dies zu tun, besteht darin, eine Auswahl und eine Verknüpfung in Ihrem Löschvorgang zu verwenden.

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

Unter keinen Umständen sollten Sie mit einer großen Tabelle wie folgt verfahren:

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

Dies führt normalerweise zu einem Antijoin in einer verschachtelten Schleife, was die Leistung ziemlich problematisch macht. Wenn Sie am Ende diesen Weg gehen müssen, tun Sie dies stattdessen:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL ist normalerweise ziemlich gut darin, schlechte Pläne zu vermeiden, aber es gibt immer noch Fälle, in denen es um äußere Verknüpfungen geht, die einen großen Unterschied zwischen guten und schlechten Plänen machen können.

Dies wandert ein wenig weiter, aber ich denke, es ist erwähnenswert, da es einfach ist, von IN nach NOT IN zu wechseln und den Performance-Tank für Abfragen zu beobachten.

Chris Travers
quelle
Das hat sehr geholfen, danke! Ich fand jedoch, dass das Kombinieren von Abfragen in diesem speziellen Fall effizienter ist. ZB IN ( select id from foo except select id from rows_to_keep ) Siehe postgresql.org/docs/9.4/static/queries-union.html
Ufos
1

Ich bin auf diese Frage gestoßen, weil ich ein ähnliches Problem hatte. Ich bereinige eine Datenbank mit mehr als 300 Millionen Zeilen. Die endgültige Datenbank enthält nur etwa 30% der ursprünglichen Daten. Wenn Sie mit einem ähnlichen Szenario konfrontiert sind, ist es tatsächlich einfacher, eine neue Tabelle einzufügen und neu zu indizieren, als sie zu löschen.

Mach sowas wie

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

Mit der richtigen Indizierung von foo und bar können Sie Seq-Scans vermeiden.

Dann müssten Sie die Tabelle neu indizieren und umbenennen.

Niro
quelle