web-dev-qa-db-fra.com

Contraintes de clé étrangère: quand utiliser ON UPDATE et ON DELETE

Je conçois mon schéma de base de données avec MySQL Workbench, ce qui est très pratique car vous pouvez créer des diagrammes et les convertir: P

Quoi qu'il en soit, j'ai décidé d'utiliser InnoDB en raison de la prise en charge de la clé étrangère. Une chose que j’ai remarquée cependant est qu’elle vous permet de définir les options On Update et Delete pour les clés étrangères. Quelqu'un peut-il expliquer où "Restrict", "Cascade" et set null pourraient être utilisés dans un exemple simple?

Par exemple, disons que j'ai une table user qui inclut un userID. Et disons que j'ai une table de messages message qui est un plusieurs-à-plusieurs qui a 2 clés étrangères (qui référencent la même clé primaire, userID dans la table user.). La définition des options On Update et On Delete est-elle utile dans ce cas? Si oui, lequel dois-je choisir? Si ce n'est pas un bon exemple, pourriez-vous, s'il vous plaît, trouver un bon exemple pour illustrer leur utilité?

Merci

178
meltuhamy

N'hésitez pas à mettre des contraintes sur la base de données. Vous serez sûr d'avoir une base de données cohérente, et c'est l'une des bonnes raisons d'utiliser une base de données. Surtout si vous avez plusieurs applications qui le demandent (ou une seule application mais avec un mode direct et un mode batch utilisant des sources différentes).

Avec MySQL, vous n’avez pas de contraintes avancées comme dans postgreSQL, mais au moins les contraintes de clé étrangère sont assez avancées.

Prenons un exemple, une table de société avec une table d’utilisateur contenant des personnes de cette société.

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

Regardons la clause ON UPDATE :

  • ON UPDATE RESTRICT : la valeur par défaut : si vous essayez de mettre à jour un company_id dans la table COMPANY, le moteur rejette l'opération si un utilisateur au moins crée des liens vers cette société.
  • ON UPDATE NO ACTION : identique à RESTRICT.
  • ON UPDATE CASCADE : généralement le meilleur : si vous mettez à jour un company_id dans une ligne de la table COMPANY le moteur le mettra à jour en conséquence sur toutes les lignes USER référençant cette société (mais aucun déclencheur activé sur la table USER, avertissement). Le moteur va suivre les changements pour vous, c'est bien.
  • ON UPDATE SET NULL : si vous mettez à jour un company_id dans une ligne de la table COMPANY, le moteur définira l'utilisateur USER associé company_id sur NULL (doit être disponible dans USER company_id champ). Je ne vois rien d'intéressant à faire avec cela dans une mise à jour, mais je peux me tromper.

Et maintenant du côté ON DELETE :

  • ON DELETE RESTRICT : la valeur par défaut : si vous essayez de supprimer un id_entreprise dans la table COMPANY du moteur rejettera l'opération si un utilisateur, au moins des liens sur cette société, peut vous sauver la vie.
  • ON DELETE NO ACTION : identique à RESTRICT
  • ON DELETE CASCADE : dangereux : si vous supprimez une ligne de société dans la table COMPANY, le moteur sera supprimé en tant que bien les utilisateurs associés. Ceci est dangereux mais peut être utilisé pour effectuer des nettoyages automatiques sur des tables secondaires (donc ce peut être quelque chose que vous voulez, mais certainement pas pour un exemple COMPANY <-> USER)
  • ON DELETE SET NULL : poignée : si vous supprimez une ligne COMPANY, les UTILISATEURS associés auront automatiquement le relation à NULL. Si Null est votre valeur pour les utilisateurs sans société, cela peut être un bon comportement. Par exemple, vous devez peut-être conserver les utilisateurs dans votre application, en tant qu'auteurs de contenu, mais la suppression de la société ne vous pose pas de problème.

la valeur par défaut est généralement: ON DELETE RESTRICT ON UPDATE CASCADE . avec quelques ON DELETE CASCADE pour les tables de pistes (journaux - pas tous les journaux, etc.) et ON DELETE SET NULL lorsque la table principale est un "attribut simple" pour la table contenant la clé étrangère, comme un Table JOB pour la table USER.

Modifier

Cela fait longtemps que j'ai écrit ça. Maintenant, je pense que je devrais ajouter un avertissement important. MySQL a une grosse limitation documentée avec les cascades. Les cascades ne déclenchent pas de déclencheurs . Donc, si vous étiez trop confiant dans ce moteur pour utiliser les déclencheurs, évitez les contraintes en cascades.

Les déclencheurs MySQL s'activent uniquement pour les modifications apportées aux tables par les instructions SQL. Ils ne s'activent pas pour les modifications de vues, ni par les modifications de tables effectuées par des API qui ne transmettent pas d'instructions SQL au serveur MySQL.

==> Voir ci-dessous la dernière modification, les choses bougent sur ce domaine

Les déclencheurs ne sont pas activés par des actions de clé étrangère.

Et je ne pense pas que cela sera réglé un jour. Les contraintes de clé étrangère sont gérées par le stockage InnoDb et les déclencheurs par le moteur MySQL SQL. Les deux sont séparés. Innodb est le seul stockage avec gestion des contraintes. Peut-être ajouteront-ils des déclencheurs directement dans le moteur de stockage un jour, peut-être pas.

Mais j’ai ma propre opinion sur l’élément à choisir entre la mise en œuvre médiocre du déclencheur et le très utile support des contraintes de clés étrangères. Et une fois que vous serez habitué à la cohérence de la base de données, vous adorerez PostgreSQL.

12/2017-Mise à jour de cette édition à propos de MySQL:

comme indiqué par @IstiaqueAhmed dans les commentaires, la situation a changé à ce sujet. Alors suivez le lien et vérifiez la situation actuelle (qui pourrait changer à nouveau dans le futur).

455
regilero

Ajout à la réponse @MarkR - une chose à noter serait que de nombreux frameworks PHP avec ORM ne reconnaissent pas ou n'utilisent pas la configuration DB avancée (clés étrangères, suppression en cascade, contraintes uniques), ce qui peut entraîner un comportement inattendu. .

Par exemple, si vous supprimez un enregistrement à l'aide d'ORM et que votre DELETE CASCADE supprimera les enregistrements des tables associées, la tentative de suppression de ces enregistrements associés (souvent automatique) entraînera une erreur.

2
lxa

Vous devrez considérer cela dans le contexte de l'application. En général, vous devez concevoir une application et non une base de données (la base de données faisant simplement partie de l'application).

Réfléchissez à la manière dont votre demande devrait répondre à divers cas.

L’action par défaut consiste à limiter (c’est-à-dire à ne pas autoriser) l’opération, ce qui est normalement ce que vous voulez, car elle évite les erreurs de programmation stupides. Cependant, sur DELETE CASCADE peut également être utile. Cela dépend vraiment de votre application et de la manière dont vous souhaitez supprimer des objets particuliers.

Personnellement, j'utiliserais InnoDB car il ne détruit pas vos données (par exemple, MyISAM), plutôt que parce qu'il a des contraintes FK.

1
MarkR