web-dev-qa-db-fra.com

PostgreSQL - désactivation des contraintes

J'ai une table avec environ 5 millions de lignes qui a une contrainte fk référençant la clé primaire d'une autre table (également environ 5 millions de lignes).

Je dois supprimer environ 75 000 lignes des deux tables. Je sais que si j'essaie de le faire avec la contrainte fk activée, cela va prendre un temps inacceptable.

Venant d'un arrière-plan Oracle, ma première pensée a été de désactiver la contrainte, de supprimer et de réactiver la contrainte. PostGres semble me permettre de désactiver les déclencheurs de contraintes si je suis un super utilisateur (je ne le suis pas, mais je me connecte en tant qu'utilisateur propriétaire/créateur des objets) mais cela ne semble pas être tout à fait ce que je veux.

L'autre option consiste à supprimer la contrainte, puis à la rétablir. Je crains que la reconstruction de la contrainte ne prenne du temps compte tenu de la taille de mes tables.

Des pensées?

edit: après les encouragements de Billy, j'ai essayé de faire la suppression sans changer aucune contrainte et cela prend plus de 10 minutes. Cependant, j'ai découvert que la table à partir de laquelle j'essaie de supprimer a une clé étrangère auto-référentielle ... dupliquée (et non indexée).

Mise à jour finale - J'ai laissé tomber la clé étrangère auto-référentielle, j'ai supprimé et l'ajoutée à nouveau. Billy a raison mais je ne peux malheureusement pas accepter son commentaire comme réponse!

42
azp74

Selon les commentaires précédents, cela devrait poser problème. Cela dit, il existe une commande qui peut être ce que vous cherchez - elle définira les contraintes sur différées afin qu'elles soient vérifiées sur COMMIT, pas à chaque suppression. Si vous ne faites qu'une seule SUPPRESSION de toutes les lignes, cela ne fera aucune différence, mais si vous le faites en morceaux, cela le fera.

SET CONSTRAINTS ALL DEFERRED

est ce que vous recherchez dans ce cas. Notez que les contraintes doivent être marquées comme DEFERRABLE avant de pouvoir être différées. Par exemple:

ALTER TABLE table_name
  ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2)
  DEFERRABLE INITIALLY IMMEDIATE;

La contrainte peut ensuite être différée dans une transaction ou une fonction comme suit:

CREATE OR REPLACE FUNCTION f() RETURNS void AS
$BODY$
BEGIN
  SET CONSTRAINTS ALL DEFERRED;

  -- Code that temporarily violates the constraint...
  -- UPDATE table_name ...
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
49
Magnus Hagander

Ce qui a fonctionné pour moi, c'est de désactiver une à une les TRIGGERS des tables qui vont être impliquées dans l'opération DELETE.

ALTER TABLE reference DISABLE TRIGGER ALL;
DELETE FROM reference WHERE refered_id > 1;
ALTER TABLE reference ENABLE TRIGGER ALL;

La solution fonctionne dans la version 9.3.16. Dans mon cas, le temps est passé de 45 minutes à 14 secondes pour exécuter les opérations DELETE.

Comme indiqué dans la section commentaires de @amphetamachine, vous aurez besoin des privilèges admin sur les tables pour effectuer cette tâche.

14
gersonZaragocin

Si tu essayes DISABLE TRIGGER ALL et obtenez une erreur comme permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger (J'ai obtenu ceci sur Amazon RDS), essayez ceci:

set session_replication_role to replica;

Si cela réussit, tous les déclencheurs qui sous-tendent les contraintes de table seront désactivés. À vous maintenant de vous assurer que vos modifications laissent la base de données dans un état cohérent!

Ensuite, lorsque vous avez terminé, réactivez les déclencheurs et les contraintes de votre session avec:

set session_replication_role to default;
5
Jonathan Fuerth

(Cette réponse suppose que votre intention est de supprimer toutes les lignes de ces tableaux, pas seulement une sélection.)

J'ai également dû le faire, mais dans le cadre d'une suite de tests. J'ai trouvé la réponse, suggéré ailleurs sur SO . Utilisez TRUNCATE TABLE comme suit:

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];

Ce qui suit supprime rapidement toutes les lignes des tables table1, table2, et table3, à condition qu'il n'y ait aucune référence à des lignes de ces tables provenant de tables non répertoriées:

TRUNCATE TABLE table1, table2, table3;

Tant que les références sont entre les tables répertoriées, PostgreSQL supprimera toutes les lignes sans souci d'intégrité référentielle. Si une table autre que celles répertoriées fait référence à une ligne de l'une de ces tables, la requête échouera.

Cependant, vous pouvez qualifier la requête afin qu'elle tronque également toutes les tables avec des références aux tables répertoriées (bien que je n'aie pas essayé cela):

TRUNCATE TABLE table1, table2, table3 CASCADE;

Par défaut, les séquences de ces tableaux ne redémarrent pas la numérotation. Les nouvelles lignes continueront avec le numéro suivant de la séquence. Pour redémarrer la numérotation des séquences:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY;
4
Joe Lapp

Mon PostgreSQL est 9.6.8.

set session_replication_role to replica;

travaille pour moi mais j'ai besoin d'une autorisation.

Je me connecte psql avec un super utilisateur.

Sudo -u postgres psql

Connectez-vous ensuite à ma base de données

\c myDB

Et courir:

set session_replication_role to replica;

Maintenant, je peux supprimer de la table avec contrainte.

2
Weekend Baf