Bonne explication du comportement en cascade (ON DELETE / UPDATE)

98

Je ne conçois pas tous les jours des schémas, mais j'essaie de configurer correctement les mises à jour / suppressions en cascade pour faciliter l'administration. Je comprends comment fonctionnent les cascades, mais je ne me souviens jamais de quelle table il s'agit.

Par exemple, si j'ai deux tables - Parentet Child- avec une clé étrangère sur Childces références Parentet ce qui a ON DELETE CASCADE, quels enregistrements déclenchent une cascade et quels enregistrements sont supprimés par la cascade? Ma première hypothèse serait que les Childenregistrements soient supprimés quand les Parentenregistrements sont supprimés, car les Childenregistrements dépendent des Parentenregistrements, mais le ON DELETEest ambigu; cela pourrait signifier supprimer l' Parentenregistrement lorsque l' Childenregistrement est supprimé ou cela pourrait signifier supprimer l' Childenregistrement lorsque le Parentest supprimé. Alors c'est quoi?

J'aimerais que la syntaxe soit ON PARENT DELETE, CASCADE, ON FOREIGN DELETE, CASCADEou quelque chose de similaire, pour lever l'ambiguïté. Quelqu'un a-t-il des mnémoniques pour s'en souvenir?

Johntron
la source

Réponses:

138

Si vous aimez les Parentet Childconditions et vous vous sentez qu'ils sont faciles à se rappeler, vous pouvez aimer la traduction de ON DELETE CASCADElaLeave No Orphans!

Ce qui signifie que lorsqu'une Parentligne est supprimée (supprimée), aucune ligne orpheline ne doit rester en vie dans la Childtable. Tous les enfants de la ligne parent sont également supprimés (supprimés). Si l'un de ces enfants a des petits-enfants (dans une autre table via une autre clé étrangère) et qu'ils sont ON DELETE CASCADEdéfinis, ceux-ci doivent également être supprimés (ainsi que tous les descendants, tant qu'un effet de cascade est défini.)

La FOREIGN KEYcontrainte elle-même pourrait également être décrite comme Allow No Orphans!(en premier lieu). Aucun Childne devrait jamais être autorisé (écrit) dans la table enfant s'il n'en a pas Parent(une ligne dans la table parent).

Par souci de cohérence, le ON DELETE RESTRICTpeut être traduit en (moins agressif). You Can't Kill Parents!Seules les lignes sans enfant peuvent être supprimées (supprimées).

ypercubeᵀᴹ
la source
3
Je sens que quelque chose manque encore dans l'analogie. Un enfant ne peut-il avoir plus d'un parent? Dans ce cas, le meurtre d'un des parents rendra-t-il l'enfant orphelin?
Jus12
7
@ Jus12 Non, les contraintes de clé étrangère ne fonctionnent qu'avec un seul parent. Ce n'est pas une bonne analogie concernant cet aspect.
Ypercubeᵀᴹ
1
@ypercube: N'est-ce pas autorisé? Order(custID, itemID, orderID)custIDfait référence à une clé primaire dans la Customerstable et itemIDfait référence à une clé primaire dans la Itemstable. Tu n'auras pas Orderdeux parents?
Jus12
4
@ Jus12 Cela est bien sûr autorisé, mais il s'agirait de 2 contraintes de clé étrangère. Ensuite, chaque enfant (commande) aurait un parent (client) et un parent (article). Les comportements des 2 FK peuvent toutefois différer. (Ainsi, par exemple, il pourrait arriver que tuer des clients tue tous leurs enfants (ordres) mais que tuer des objets ne tue pas leurs ordres.)
ypercubeᵀᴹ
1
L'analogie parent peut toujours fonctionner si nous ne disons pas "orphelin". S'il y a deux références à deux parents séparés lors de l'entrée d'un enfant, cela peut toujours être vu comme un enfant d'un couple divorcé. Restrict: "Je ne te laisserai pas tuer ma mère" Cascade: "Si tu tues mon père, je mourrai aussi"
Christopher McGowan Le
31

Par exemple, si j'ai deux tables - Parent et Child - où les enregistrements Child appartiennent à des enregistrements Parent, quelle table a besoin de ON DELETE CASCADE?

ON DELETE CASCADE est une clause facultative dans une déclaration de clé étrangère. Donc, cela va avec la déclaration de clé étrangère. (Signification, dans la table "enfant".)

... cela pourrait signifier supprimer l'enregistrement parent lorsque l'enregistrement enfant est supprimé ou cela pourrait signifier supprimer l'enregistrement enfant lorsque le parent est supprimé. Alors c'est quoi?

Une façon d'interpréter une déclaration de clé étrangère est la suivante: "Toutes les valeurs valides pour cette colonne proviennent de 'that_column' dans 'that_table'." Lorsque vous supprimez une ligne dans la table "enfant", personne ne s'en soucie. Cela n'affecte pas l'intégrité des données.

Lorsque vous supprimez une ligne de la table "parent" - de "that_table" - vous supprimez une valeur valide des valeurs possibles pour la table "enfant". Pour maintenir l'intégrité des données, vous devez modifier la table "enfant". Les suppressions en cascade sont une chose que vous pouvez faire.

Mike Sherrill 'Rappel de chat'
la source
2

SQL: Spéc. 2011

Il y a cinq options pour ON DELETE, et ON UPDATEcela peut s’appliquer à FOREIGN KEY. Celles-ci sont appelées <referential actions>directement à partir de la spécification SQL: 2011

  • ON DELETE CASCADE: si une ligne de la table référencée est supprimée, toutes les lignes correspondantes de la table référencée sont supprimées.
  • ON DELETE SET NULL: si une ligne de la table référencée est supprimée, toutes les colonnes de référence de toutes les lignes correspondantes de la table de référence doivent être définies sur null.
  • ON DELETE SET DEFAULT: si une ligne de la table référencée est supprimée, toutes les colonnes de référence de toutes les lignes correspondantes de la table de référence doivent être définies sur la valeur par défaut de la colonne.
  • ON DELETE RESTRICT: il est interdit de supprimer une ligne de la table référencée si cette ligne a des lignes correspondantes dans la table de référence.
  • ON DELETE NO ACTION (valeur par défaut) : il n’existe aucune action de suppression référentielle; la contrainte référentielle spécifie uniquement une vérification de contrainte.

La clé étrangère établit la relation dépendante. Le <referential action>détermine ce qui se passe lorsque la relation est dissoute.

Exemple / métaphore / explication

Pour cet exemple, nous accepterons le modèle commun de société et d’économie: chaque entreprise businessest une entreprise qui entretient une relation de bout en bourgeoisiebout fatcat_owner.

CREATE TABLE bourgeoisie(
  fatcat_owner varchar(100) PRIMARY KEY
);
INSERT INTO bourgeoisie(fatcat_owner) VALUES
  ( 'Koch Brothers' );

CREATE TABLE business (
  name         varchar(100),
  fatcat_owner varchar(100) REFERENCES bourgeoisie
);
INSERT INTO business(name, fatcat_owner)
  VALUES ('Georgia-Pacific', 'Koch Brothers');

Si toutes les businesses sont directement touchées par la bourgeoisieleur, fatcat_ownerque faites-vous après la révolution ouvrière lorsque vous la purgez fatcat_owneret que vous avez une société sans classes?

-- Viva la revolución 
BEGIN;
  DELETE FROM bourgeoisie;
END;

Vous avez quelques options ici,

  • Arrête la révolution. Dans le langage SQL, RESTRICT. Certaines personnes croient que c'est le moindre mal, mais ils ont généralement tort.
  • Laissez-le continuer. Si oui, quand la révolution se produit, SQL vous donne quatre options,

    • SET NULL- laissez le champ vide. Qui sait, peut-être que le capitalisme est restauré bourgeoisieet que les oligarques remplissent les rôles fatcat_owners. Remarque importante, la colonne doit être NULLABLE(non NOT NULL) ou cela ne peut jamais arriver.
    • SET DEFAULT- Peut-être que vous avez eu un DEFAULTqui a géré cela? Un DEFAULTpeut appeler une fonction. Peut-être que votre schéma est déjà prêt pour la révolution.
    • CASCADE- il n'y a pas de contrôle des dégâts. Si le bourgeoisieva, le fait aussi business. Si une entreprise doit en avoir un fatcat_pig, il est parfois plus logique de perdre les données plutôt que de laisser un non-entreprise dans une businesstable.
    • NO ACTION- c’est essentiellement une méthode pour retarder la vérification, cela n’est pas différent dans MySQL RESTRICT, mais dans PostgreSQL, vous pouvez le faire.

      -- Not a real revolution.
      -- requires constraint be DEFERRABLE INITIALLY DEFERRED
      BEGIN;
        SET CONSTRAINTS ALL DEFERRED;
        DELETE FROM bourgeoisie;
        INSERT INTO bourgeoisie VALUES ( 'Putin' );
        UPDATE business SET fatcat_pig = 'Putin';
      END;

      Dans un tel système, la contrainte n'est validée qu'avant la validation de la transaction. Cela peut entraîner l’arrêt de la révolution, mais vous pouvez récupérer dans la transaction, moyennant un certain degré de «récupération».

Evan Carroll
la source
Est referenced- ce que table signifie table parent et referencingtable signifie table enfant?
sg552
@ sg552 Oui, vous l'avez bien compris.
informatik01
1

Un simple mnémonique serait

ON DELETE du parent CASCADE [en supprimant] ici

Cela vous indique quels suppressions (suppressions du parent) sont mises en cascade, où l' instruction ON DELETE CASCADE est envoyée (sur l'enfant) et ce qui est supprimé (l'enfant).

msouth
la source
-3

Eh bien, peut-être pouvons-nous rationaliser la syntaxe. Prenons un exemple en Python:

class Parent(self):
    # define parent's fields

class Child(self):    
    # define child's fields
    parent_pk_is_childs_foreign_key = models.ForeignKey(Parent, on_delete=models.CASCADE)

ce que cette ligne dit est on_delete du parent (ce qui est mentionné accidentellement dans la déclaration), veuillez transférer en cascade la suppression sur l'enfant. C’est pourquoi l’instruction CASCADE est définie au niveau de l’enfant, elle marque les enfants à supprimer.

Par exemple si vous avez eu une autre classe

class GrownUpChild(self):    
        # define grown up child's fields
        parent_pk_is_childs_foreign_key = models.ForeignKey(Parent, on_delete=models.DO_NOTHING)

cette structure indiquerait clairement lequel des enfants doit être retiré (Child) et ceux qui doivent rester (GrownUpChild), même orphelin

[Edit: Etant donné le contexte de la discussion, en particulier dans les cas de on_delete = models.CASCADE, etc.], en fait, il est souvent souhaitable de laisser les enfants d'un parent supprimé, pour des raisons d'audit et de rapport, ainsi que pour la récupération accidentelle. suppressions. [bien entendu, les logiciels de niveau entreprise seront construits autour de ce comportement et marqueront les enregistrements supprimés comme supprimés = 1 au lieu de les supprimer réellement et ne les incluront pas non plus dans les requêtes pour le serveur frontal, à l'exception de certains rapports spécialement conçus. De plus, il aura pour fonction de purger les enregistrements supprimés == 1 de la base de données, qui seront généralement exécutés par l'administrateur de l'interface utilisateur, évitant souvent toute implication de son côté.]

George Mogilevsky
la source
1
«En fait, il est souvent souhaitable de laisser les enfants d'un parent supprimé, pour des raisons d'audit et de rapport, ainsi que de récupérer des suppressions accidentelles» , ce qui serait désastreux dans une base de données (saine).
dezso
@dezso merci pour votre contribution. Cependant, plusieurs systèmes de CRM de niveau entreprise font exactement cela.
George Mogilevsky
TBH qui ne le rend pas plus sensible. Une fois, j'ai eu la tâche de réparer la merde résultant d'une telle approche - pas de joie, sauf le salaire.
dezso
vous ressemblez à un administrateur de base de données avisé :) Je peux tout à fait entendre votre point. Le logiciel que j'ai mentionné ci-dessus qui le fait, a en fait une fonction pour supprimer ceux supprimés = 1 manuellement, c'est donc à l'administrateur de l'application de faire cet appel. Normalement, l'administrateur de base de données n'est même pas impliqué dans le maintien de cet aspect. Et en outre, la classe de base de données du logiciel entier est construite autour du concept, de sorte qu'il vérifie toujours l'indicateur supprimé dans les opérations crud
George Mogilevsky
Oui, c'est un modèle connu et sain, mais vous devriez alors éventuellement modifier le libellé ci-dessus pour refléter cela.
dezso