web-dev-qa-db-fra.com

Comment supprimer des données de plusieurs tables?

J'ai ces tableaux:

un événement     (evt_id, evt_code, reg_id) 
 
ordre de grandeur (mag_id, evt_id, valeur) 
 
trace     (trace_id, pt_id) 
 
point     (pt_id, evt_id) 

Je souhaite supprimer toutes les lignes de toutes les tables liées à evt_id=1139.
Comment fait-on ça?

22
user1202766

Si vous avez le contrôle sur votre schéma, je ferais en sorte que le schéma utilise suppressions en cascade .

Extrait de l'article (la partie la plus pertinente traduite pour votre exemple)

CREATE TABLE point
(
    pt_id integer PRIMARY KEY,
    evt_id integer REFERENCES event ON DELETE CASCADE
)

Si vous avez configuré des cascades, vous pouvez simplement supprimer de la table d'événements principale et toutes les autres tables seront nettoyées automatiquement

Sinon, vous devez d'abord supprimer toutes les références, puis vous supprimez la table principale. Vous devez le faire en une seule transaction pour garder les données cohérentes

BEGIN;
DELETE FROM trace WHERE EXISTS 
    (SELECT 1 FROM point WHERE evt_id = 1139 AND trace.pt_id = point.pt_id);
DELETE FROM point where evt_id = 1139;
DELETE FROM magnitude where evt_id = 1139;
DELETE FROM event where evt_id = 1139;
COMMIT;
19
Justin Pihony

Le seul élément non trivial de votre question est la suppression de la table trace. Je suppose qu'il est sûr de supposer trace.pt_id fait référence à point.pt_id?

Soit vous définissez une clé étrangère avec ON DELETE CASCADE et oubliez simplement la table trace (comme déjà souligné par @kevin ) ou vous devez prendre soin de dépendre les lignes manuellement.

Depuis PostgreSQL 9.1 vous pouvez utiliser CTE modifiant les données :

BEGIN;

WITH x AS (
    DELETE FROM point WHERE evt_id = 1139
    RETURNING pt_id
    )
DELETE FROM trace
USING x
WHERE trace.pt_id = x.pt_id;

DELETE FROM magnitude WHERE evt_id = 1139;

DELETE FROM event WHERE evt_id = 1139;

COMMIT;

clause RETURNING de DELETE FROM point renvoie tous les éléments concernés pt_id - ceux-ci sont à leur tour utilisés pour supprimer toutes les lignes correspondantes de trace.

Vous n'avez pas mentionné si simultané est un problème dans votre cas. Si tel est le cas, et si vous souhaitez éviter les résultats possibles dans la courte fenêtre de temps entre les suppressions où les lignes pour evt_id = 1139 sont présents dans une table alors qu'ils sont déjà supprimés d'une autre, enveloppez le tout dans une transaction.
Si cela ne vous concerne pas, ignorez BEGIN et COMMIT.

Pour éviter deadlocks utilisez toujours la même séquence d'instructions de suppression (dans toutes les transactions simultanées). Cela évite les situations où une transaction commencerait à être supprimée dans une table, la suivante dans l'autre table et ensuite chacune attendrait l'autre.

14
Erwin Brandstetter

Si les lignes que vous souhaitez supprimer peuvent être envisagées comme une hiérarchie, vous pouvez définir des contraintes FOREIGN KEY pour les relations avec la clause ON DELETE CASCADE. La suppression de la ligne en haut entraînera une cascade vers le bas et les supprimera tous.

http://www.postgresql.org/docs/9.1/static/ddl-constraints.html#DDL-CONSTRAINTS-FK

5
kgrittn