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

196

Je conçois mon schéma de base de données à l'aide de MySQL Workbench, ce qui est plutôt cool car vous pouvez faire des diagrammes et il les convertit: 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, c'est qu'elle vous permet de définir les options de mise à jour et de suppression 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 usertable qui comprend un userID. Et disons que j'ai une table de messages messagequi est un plusieurs-à-plusieurs qui a 2 clés étrangères (qui font référence à la même clé primaire, userIDdans la usertable). La définition des options Sur mise à jour et Sur suppression est-elle utile dans ce cas? Si oui, lequel 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

Meltuhamy
la source

Réponses:

485

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 le demandant (ou juste une application mais avec un mode direct et un mode batch utilisant différentes sources).

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

Prenons un exemple, une table d'entreprise avec une table utilisateur contenant des personnes de cette entreprise

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 rejettera l'opération si au moins un UTILISATEUR établit un lien sur cette société.
  • À LA MISE À JOUR PAS D'ACTION : identique à RESTRICT.
  • ON UPDATE CASCADE : le meilleur habituellement : 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 COMPANY (mais aucun déclencheur activé sur la table USER, avertissement). Le moteur suivra les changements pour vous, c'est bon.
  • ON UPDATE SET NULL : si vous mettez à jour un company_id dans une ligne de la table COMPANY, le moteur définira les USER associés company_id sur NULL (devrait être disponible dans le champ USER company_id). Je ne vois aucune chose intéressante à faire avec cela sur 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 company_id dans la table COMPANY, le moteur rejettera l'opération si un USER au moins établit un lien sur cette société, peut vous sauver la vie.
  • SUR SUPPRIMER AUCUNE ACTION : identique à RESTRICT
  • ON DELETE CASCADE : dangereux : si vous supprimez une ligne de société dans la table COMPANY, le moteur supprimera également les USER associés. Ceci est dangereux mais peut être utilisé pour effectuer des nettoyages automatiques sur les tables secondaires (donc cela peut être quelque chose que vous voulez, mais certainement pas pour un exemple COMPANY <-> USER)
  • ON DELETE SET NULL : handful : si vous supprimez une ligne COMPANY, les USER associés auront automatiquement la relation avec NULL. Si Null est votre valeur pour les utilisateurs sans entreprise, cela peut être un bon comportement, par exemple, vous devez peut-être garder les utilisateurs dans votre application, en tant qu'auteurs de certains contenus, mais la suppression de l'entreprise n'est pas un problème pour vous.

généralement ma valeur par défaut est: ON DELETE RESTRICT ON UPDATE CASCADE . avec certains ON DELETE CASCADEpour les tables de suivi (journaux - pas tous les journaux -, des choses comme ça) et ON DELETE SET NULLlorsque la table principale est un `` attribut simple '' pour la table contenant la clé étrangère, comme une table JOB pour la table USER.

Éditer

Cela fait longtemps que je n'ai pas é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 aviez trop confiance en ce moteur pour utiliser les déclencheurs, vous devriez éviter les contraintes de cascades.

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

==> Voir ci-dessous la dernière édition, 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és étrangères sont gérées par le stockage InnoDb et les déclencheurs sont gérés par le moteur SQL MySQL. Les deux sont séparés. Innodb est le seul stockage avec gestion des contraintes, peut-être qu'ils ajouteront des déclencheurs directement dans le moteur de stockage un jour, peut-être pas.

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

12/2017-Mise à jour de cette modification sur MySQL:

comme indiqué par @IstiaqueAhmed dans les commentaires, la situation a changé à ce sujet. Suivez donc le lien et vérifiez la situation actuelle (qui pourrait changer à nouveau à l'avenir).

regilero
la source
8
ON DELETE CASCADE : dangerous- prenez avec une pincée de sel.
quand
3
Vous devrez faire attention à la cascade, cela peut verrouiller votre système si de nombreux enregistrements doivent être modifiés. La suppression de Cascde doit être particulièrement examinée attentivement avant utilisation, souvent vous voulez vraiment que la suppression ne se produise pas s'il existe des enregistrements enfants. Je ne voudrais pas qu'un client supprime pour effacer les données financières des oreders qu'il avait précédemment. Parfois, il est préférable de s'assurer que le cacading n'est pas activé et de fournir un moyen de modifier les enregistrements comme inactifs.
HLGEM
1
En termes de logique métier, il y a un cas qui pourrait être intéressant SET NULLdans un ON UPDATE: la mise à jour d'une entreprise représente un détachement de la relation Entreprise> Utilisateur. Par exemple: si une entreprise change de type d'entreprise, les utilisateurs précédents peuvent ne plus être liés à cette entreprise et NULLpeuvent donc être préférables pour cet indice.
CPHPython
1
@regilero, il semble que le contenu de votre premier lien ( dev.mysql.com/doc/refman/5.6/en/triggers.html ) vers le site mysql a changé. Il dit à la This includes changes to base tables that underlie updatable viewsplace de ce que vous avez collé, c'estThey do not activate for changes in views
Istiaque Ahmed
6
"Je ne voudrais pas qu'un client supprime efface les données financières des commandes qu'il avait précédemment." Dans une situation comme celle-là, vous avez probablement toujours besoin des données du client. Votre conception doit probablement marquer le client comme inactif, et non supprimer sa ligne de la base de données. En pratique, selon mon expérience professionnelle, il est très rare que vous souhaitiez supprimer quoi que ce soit , préférant marquer inactif par défaut. Dans les cas où la suppression définitive est correcte, CASCADE DELETEest généralement également correcte, même préférée. Je ne le considère pas comme particulièrement dangereux.
GrandOpener
3

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 avancée de la base de données (clés étrangères, suppression en cascade, contraintes uniques), et cela peut entraîner un comportement inattendu.

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

lxa
la source
11
Ce serait une raison de ne pas utiliser cet ORM particulier. Tout outil qui est si pauvre au niveau du support de la base de données n'est pas fiable. Les clés étrangères et les suppressions ou mises à jour en cascade ne sont pas des concepts avancés et aucune base de données réelle ne doit jamais être conçue sans contraintes de clé étrangère!
HLGEM
Le problème est qu'ils jettent des erreurs. Est-il possible de RESTREINDRE LES SUPPRIMÉS mais le moteur ne génère-t-il pas d'erreurs tout en conservant la sémantique? Je souhaite que mon programme continue de fonctionner tout en protégeant les autres données contre la suppression.
TheRealChx101
2

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

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

L'action par défaut est de restreindre (c'est-à-dire de ne pas autoriser) l'opération, qui est normalement ce que vous voulez car elle empêche les erreurs de programmation stupides. Cependant, sur DELETE CASCADE peut également être utile. Cela dépend vraiment de votre application et de la façon dont vous avez l'intention de supprimer des objets particuliers.

Personnellement, j'utiliserais InnoDB parce qu'il ne jette pas vos données (cf MyISAM, qui le fait), plutôt que parce qu'il a des contraintes FK.

MarkR
la source