web-dev-qa-db-fra.com

Supprimer des lignes avec une clé étrangère dans PostgreSQL

Je voudrais supprimer les lignes qui contiennent une clé étrangère, mais quand j'essaye quelque chose comme ceci:

DELETE FROM osoby WHERE id_osoby='1'

Je reçois cette déclaration:

ERREUR: la mise à jour ou la suppression de la table "osoby" ne respecte pas la contrainte de clé étrangère "kontakty_ibfk_1" de la table "kontakty" DETAIL: La clé (id_osoby) = (1) est toujours référencée depuis la table "kontakty".

Comment puis-je supprimer ces lignes?

55
Michal Loksik

Pour automatiser cela, vous pouvez définir la contrainte de clé étrangère avec ON DELETE CASCADE.
Je cite le le manuel des contraintes de clé étrangère :

CASCADE spécifie que, lorsqu'une ligne référencée est supprimée, la ou les lignes référençant celle-ci doivent également être automatiquement supprimées.

Recherchez la définition actuelle de FK comme ceci:

SELECT pg_get_constraintdef(oid) AS constraint_def
FROM   pg_constraint
WHERE  conrelid = 'public.kontakty'::regclass  -- assuming pubic schema
AND    conname = 'kontakty_ibfk_1';

Puis ajoutez ou modifiez le ON DELETE ... partie à ON DELETE CASCADE (en conservant tout le reste tel quel) dans une déclaration telle que:

ALTER TABLE kontakty
   DROP CONSTRAINT kontakty_ibfk_1
 , ADD  CONSTRAINT kontakty_ibfk_1
   FOREIGN KEY (id_osoby) REFERENCES osoby (id_osoby) ON DELETE CASCADE;

Comme il n'y a pas de ALTER CONSTRAINT _ syntaxe, supprime et recrée la contrainte en un seul ALTER TABLE déclaration. Cela évite les situations de concurrence possibles avec un accès en écriture simultané.

Vous avez évidemment besoin des privilèges pour le faire. L'opération prend un ACCESS EXCLUSIVE verrouiller la table kontakty et un SHARE ROW EXCLUSIVE verrouiller la table osoby.

Si vous ne pouvez pas ALTER la table, supprimez-la à la main (une fois) ou par le déclencheur BEFORE DELETE (à chaque fois) sont les options restantes.

66
Erwin Brandstetter

Vous ne pouvez pas supprimer une clé étrangère si elle fait toujours référence à une autre table. Supprimez d'abord la référence

delete from kontakty
where id_osoby = 1;

DELETE FROM osoby 
WHERE id_osoby = 1;
31
juergen d

Cela ne devrait pas être recommandé en tant que solution générale, mais vous pouvez éventuellement désactiver temporairement les déclencheurs des tables en question en cas de suppression ponctuelle de lignes d'une base de données non en production ou en cours d'utilisation.

Dans mon cas, je suis en mode de développement et j'ai quelques tables qui se référencent via des clés étrangères. Ainsi, supprimer leur contenu n'est pas aussi simple que de supprimer toutes les lignes d'une table avant l'autre. Donc, pour moi, cela a bien fonctionné de supprimer leur contenu comme suit:

ALTER TABLE table1 DISABLE TRIGGER ALL;
ALTER TABLE table2 DISABLE TRIGGER ALL;
DELETE FROM table1;
DELETE FROM table2;
ALTER TABLE table1 ENABLE TRIGGER ALL;
ALTER TABLE table2 ENABLE TRIGGER ALL;

Vous devriez pouvoir ajouter les clauses WHERE comme vous le souhaitez, en veillant bien entendu à ne pas porter atteinte à l'intégrité de la base de données.

Il y a de bonnes discussions connexes sur http://www.openscope.net/2012/08/23/subverting-foreign-key-constraints-in-postgres-or-mysql/

16
Andrew Basile

Cela fait longtemps que cette question a été posée, l'espoir peut aider. Parce que vous ne pouvez pas changer ou altérer la structure de la base de données, vous pouvez le faire. selon le postgresql docs .

[~ # ~] tronque [~ # ~] - vide un tableau ou un ensemble de tableaux.

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

Description

TRUNCATE supprime rapidement toutes les lignes d'un ensemble de tables. Cela a le même effet qu'un DELETE non qualifié sur chaque table, mais comme il n'analyse pas réellement les tables, il est plus rapide. De plus, il récupère immédiatement l’espace disque, au lieu de nécessiter une opération VACUUM ultérieure. Ceci est le plus utile sur les grandes tables.


Tronquez la table othertable et passez en cascade à toutes les tables référençant une autre table via des contraintes de clé étrangère:

TRUNCATE othertable CASCADE;

Le même, et réinitialiser tous les générateurs de séquence associés:

TRUNCATE bigtable, fattable RESTART IDENTITY;

Tronquez et réinitialisez tous les générateurs de séquence associés:

TRUNCATE revinfo RESTART IDENTITY CASCADE ;
10
OJVM

Cela signifie que dans la table kontakty vous avez une ligne référençant la ligne dans osoby que vous voulez supprimer. Vous devez d'abord supprimer cette ligne ou définir une suppression en cascade sur la relation entre les tables.

Powodzenia!

6
zibi