Il modo più efficiente di eliminare in blocco le righe da Postgres


23

Mi chiedo quale sia il modo più efficiente per eliminare un gran numero di righe da PostgreSQL, questo processo sarebbe parte di un'attività ricorrente ogni giorno per importare in blocco i dati (un delta di inserzioni + eliminazioni) in una tabella. Potrebbero esserci migliaia, potenzialmente milioni di righe da eliminare.

Ho un file di chiavi primarie, una per riga. Le due opzioni a cui stavo pensando erano sulla falsariga di quanto sotto, ma non conosco / capisco abbastanza gli interni di PostgreSQL per prendere una decisione informata che sarebbe meglio.

  • Esegui una DELETEquery per ogni riga nel file, con una semplice WHEREchiave primaria (o raggruppa le eliminazioni in batch nutilizzando una IN()clausola)
  • Importa le chiavi primarie in una tabella temporanea usando il COPYcomando e quindi eliminando dalla tabella principale usando un join

Ogni suggerimento sarà molto apprezzato!


1
La stessa domanda ha avuto risposta in modo più dettagliato qui: stackoverflow.com/a/8290958
Simon,

Risposte:


25

La tua seconda opzione è molto più pulita e si comporterà abbastanza bene da valerne la pena. La tua alternativa è costruire query gigantesche che saranno piuttosto difficili da pianificare ed eseguire. In generale, sarà meglio lasciare che PostgreSQL faccia il lavoro qui. In generale, ho trovato aggiornamenti su decine di migliaia di righe nel modo in cui descrivi per funzionare in modo adeguato, ma c'è una cosa importante da evitare.

Il modo per farlo è utilizzare una selezione e un join nella tua eliminazione.

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

In nessun caso dovresti seguire una tabella di grandi dimensioni:

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

Questo di solito causerà un antijoin ad anello nidificato che renderà le prestazioni piuttosto problematiche. Se finisci per seguire questa strada, fai invece:

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 è in genere abbastanza bravo a evitare piani sbagliati, ma ci sono ancora casi che coinvolgono join esterni che possono fare una grande differenza tra piani buoni e piani cattivi.

Questo sta vagando un po 'più lontano, ma immagino che valga la pena menzionarlo a causa di quanto sia facile passare da IN a NOT IN e guardare il serbatoio delle prestazioni delle query.


Ciò ha aiutato molto, grazie! Tuttavia, ho scoperto che l'utilizzo di "combinazione di query" è più efficiente in questo caso particolare. Ad esempio, IN ( select id from foo except select id from rows_to_keep ) vedi postgresql.org/docs/9.4/static/queries-union.html
Ufos,

1

Mi sono imbattuto in questa domanda perché avevo un problema simile. Sto ripulendo un database con oltre 300 milioni di righe, il database finale avrà solo circa il 30% dei dati originali. Se stai affrontando uno scenario simile, in realtà è più semplice inserire in una nuova tabella e reindicizzare invece di eliminarlo.

Fai qualcosa di simile

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

Con una corretta indicizzazione su foo e bar, puoi evitare scansioni Seq.

Quindi dovresti reindicizzare e rinominare la tabella.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.