J'ai deux foreign keys dans une table. Supposons que cette table s'appelle News
et possède des foreign keys updatedById
et createdById
, qui pointent vers userId
dans la table Users
.
Maintenant, je veux mettre à NULL
les foreign keys lorsque l'user est supprimé, mais quand j'essaie de définir ON DELETE SET NULL
dans ces relations, je reçois:
L'introduction de la contrainte FOREIGN KEY 'FK_News_Users' sur la table 'News' peut provoquer des cycles ou plusieurs paths en cascade. Spécifiez ON DELETE NO ACTION ou ON UPDATE NO ACTION ou modifiez d'autres contraintes FOREIGN KEY.
Je ne comprends pas pourquoi les deux foreign keys ne peuvent pas mettre à null?
Actions en cascade multiples
La série d'actions référentielles en cascade déclenchée par un seul DELETE ou UPDATE doit former un tree ne contenant pas de references circulaires. Aucune table ne peut apparaître plus d'une fois dans la list de toutes les actions référentielles en cascade qui résultent de DELETE ou UPDATE. En outre, l'tree des actions référentielles en cascade ne doit pas avoir plus d'un path vers une table spécifiée. Toute twig de l'tree est terminée lorsqu'il rencontre une table pour laquelle aucune action n'a été spécifiée ou est la valeur par défaut.
Peut-être dans des situations comme celle-ci, vous pouvez envisager d'implémenter des fonctionnalités pour supprimer l'user plutôt que physiquement (par exemple en introduisant un champ d'indicateur Active ou Supprimé dans le tableau des Users
). De cette façon, toutes les relations restnt intactes et peuvent être analysées rétrospectivement.
Mais si vous avez encore besoin d'implémenter ON DELETE SET NULL
pour les deux FK, vous pouvez utiliser un triggersur FOR DELETE
sur la table User
comme ceci:
CREATE TRIGGER Users_News_Delete_Trigger ON Users FOR DELETE AS BEGIN UPDATE News SET createdById = NULL WHERE createdById = DELETED.id; UPDATE News SET updatedById = NULL WHERE updatedById = DELETED.id; END
Une alternative est de créer une table de reference croisée entre la table A et la table B où chaque input est A.ID et B.ID et B.ID a une key étrangère à B. Ensuite, vous pouvez simplement supprimer CASCADE à la reference croisée. Vous devrez mettre un troisième champ dans votre reference croisée pour indiquer le but unique de reference tel que
[NewsID] INT NOT NULL DEFAULT 0, [UsersID] INT NOT NULL DEFAULT 0, [IsCreatedBy] bit NOT NULL DEFAULT 0
Naturellement, vous devez alors retirer ces champs de la table A. La jointure à gauche vous donnera alors la valeur null pour ces champs s'ils sont manquants.
Je ne pense pas qu'il soit possible (dans SQL Server) de le faire sur 2 contraintes FK ou plus sur la même table, pointant vers le même FK.
Normalement, dans des situations comme celle-ci, vous préférez supprimer l'user logiquement puis physiquement en introduisant un champ indicateur (par exemple, Actif ou Supprimé). De cette façon, toutes les relations restnt intactes et peuvent être analysées rétrospectivement. — peterm
Si vous voulez conserver l'idée originale de définir NULL, un moyen de contourner le problème consisterait à gérer la suppression des users dans une procédure stockée et à effectuer les mises à jour immédiatement après.
CREATE PROCEDURE sp_DeleteUser @UserId INT AS BEGIN SET NOCOUNT ON; DELETE FROM Users WHERE Id = @UserId; UPDATE News SET created_byId = NULL WHERE created_byId = @UserId; UPDATE News SET updated_byId = NULL WHERE created_byId = @UserId; END GO