Je me demande quel serait le moyen le plus efficace de supprimer un grand nombre de lignes de PostgreSQL, ce processus ferait partie d'une tâche récurrente chaque jour pour importer en masse des données (un delta d'insertions + suppressions) dans une table. Il pourrait y avoir des milliers, voire des millions de lignes à supprimer.
J'ai un fichier de clés primaires, un par ligne. Les deux options auxquelles je pensais étaient dans le sens de ce qui suit, mais je ne connais pas/ne comprends pas suffisamment les éléments internes de PostgreSQL pour prendre une décision éclairée qui serait la meilleure.
DELETE
pour chaque ligne du fichier, avec un simple WHERE
sur la clé primaire (ou regroupez les suppressions en lots de n
à l'aide d'une fonction IN()
clause)COPY
, puis supprimez-les de la table principale à l'aide d'une jointureToutes les suggestions seront très appréciées!
Votre deuxième option est beaucoup plus propre et fonctionnera assez bien pour que cela en vaille la peine. Votre alternative est de construire de gigantesques requêtes qui seront assez pénibles à planifier et à exécuter. En général, vous feriez mieux de laisser PostgreSQL faire le travail ici. En général, j'ai trouvé des mises à jour sur des dizaines de milliers de lignes de la manière que vous décrivez pour fonctionner correctement, mais il y a une chose importante à éviter.
La façon de le faire est d'utiliser une sélection et une jointure dans votre suppression.
DELETE FROM foo WHERE id IN (select id from rows_to_delete);
Vous ne devez en aucun cas procéder comme suit avec une grande table:
DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);
Cela provoquera généralement une boucle anti-jointure imbriquée qui rendra les performances plutôt problématiques. Si vous finissez par emprunter cette voie, faites-le à la place:
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 est généralement assez bon pour éviter les mauvais plans mais il existe encore des cas impliquant des jointures externes qui peuvent faire une grande différence entre les bons et les mauvais plans.
Cela se promène un peu plus loin, mais je pense qu'il convient de le mentionner en raison de la facilité avec laquelle il est possible de passer de IN à NOT IN et de regarder le réservoir de performances des requêtes.
Je suis tombé sur cette question parce que j'avais un problème similaire. Je nettoie une base de données contenant plus de 300 millions de lignes, la base de données finale ne contiendra qu'environ 30% des données d'origine. Si vous êtes confronté à un scénario similaire, il est en fait plus facile d'insérer dans une nouvelle table et de réindexer au lieu de supprimer.
Faites quelque chose comme
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);
Avec une indexation correcte sur foo et bar, vous pouvez éviter les analyses Seq.
Ensuite, vous devrez réindexer et renommer la table.