it-swarm.com.de

Die effizienteste Methode zum Löschen von Zeilen aus Postgres

Ich frage mich, was der effizienteste Weg wäre, eine große Anzahl von Zeilen aus PostgreSQL zu löschen. Dieser Prozess wäre Teil einer jeden Tag wiederkehrenden Aufgabe, Daten (ein Delta aus Einfügungen + Löschungen) in eine Tabelle zu importieren. Es können Tausende, möglicherweise Millionen von Zeilen gelöscht werden.

Ich habe eine Datei mit Primärschlüsseln, einen pro Zeile. Die beiden Optionen, an die ich dachte, waren wie folgt, 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 DELETE Abfrage für jede Zeile in der Datei mit einem einfachen WHERE für den Primärschlüssel aus (oder gruppieren Sie die Löschvorgänge in Stapeln von n mit einem IN(). Klausel)
  • Importieren Sie die Primärschlüssel mit dem Befehl COPY in eine temporäre Tabelle und löschen Sie sie dann mit einem Join aus der Haupttabelle

Anregungen werden sehr geschätzt!

25
tarnfeld

Ihre zweite Option ist weitaus sauberer und funktioniert gut genug, damit sich dies lohnt. Ihre Alternative besteht darin, gigantische Abfragen zu erstellen, deren Planung und Ausführung ziemlich mühsam ist. Im Allgemeinen ist es besser, wenn Sie PostgreSQL hier arbeiten lassen. Im Allgemeinen habe ich Aktualisierungen für Zehntausende von Zeilen in der von Ihnen beschriebenen Weise gefunden, um eine angemessene Leistung zu erzielen. Es gibt jedoch eine wichtige Sache, die Sie vermeiden sollten.

Die Möglichkeit besteht darin, beim Löschen eine Auswahl und einen Join 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 vorgehen:

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

Dies führt normalerweise zu einem Antijoin mit verschachtelter Schleife, was die Leistung ziemlich problematisch macht. Wenn Sie diesen Weg gehen müssen, gehen Sie stattdessen folgendermaßen vor:

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 mit äußeren Verknüpfungen, die einen großen Unterschied zwischen guten und schlechten Plänen machen können.

Dies wandert etwas weiter weg, aber ich denke, es ist erwähnenswert, weil es so einfach ist, vom IN zum NOT IN zu wechseln und den Abfrageleistungstank zu beobachten.

26
Chris Travers

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 Originaldaten. Wenn Sie mit einem ähnlichen Szenario konfrontiert sind, ist es tatsächlich einfacher, eine neue Tabelle einzufügen und neu zu indizieren, anstatt sie zu löschen.

Mach so etwas

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.

2
Niro