Quelle est la manière recommandée de gérer les contraintes d'essayage de l'entreprise à références auto-références dans SQL-Server? P>
Modèle de table: P>
p>
"L'instruction DELETE en conflit avec la même référence de table
contrainte "fk_tabdataprev_tabdatanext". Le conflit s'est produit dans
Base de données "mydatabase", table "dbo.tabdata", colonne 'Fidata' " P>
blockQuote>
Si Je n'ai pas besoin de cascade supprimer des enregistrements référencés mais je devrais définir merci. p> Fidata code> fait référence à un enregistrement précédent dans Tabdata. Si je supprime un enregistrement qui est référencé par
fidata code>, la base de données jette une exception: p>
appliquer la contrainte d'entreprise d'entreprise code> est défini sur "Oui". P>
fidata = null code> où il est référencé. Mon idée est de définir
d'appliquer la contrainte d'entreprise étrangère code> à "non" et de créer un déclencheur de suppression. Est-ce recommandé ou y a-t-il de meilleurs moyens? P>
3 Réponses :
Les déclencheurs ajoutent une complexité implicite. Dans une base de données avec des déclencheurs, vous ne saurez pas ce qu'est une instruction SQL en le regardant. Dans mon expérience, les déclencheurs sont une mauvaise idée sans exception.
Dans votre exemple, définissez la contrainte forcée au "non" signifie que vous pouvez ajouter un identifiant inexistant. Et l'optimiseur de requête sera moins efficace car il ne peut pas supposer que la clé est valide. P>
envisagez de créer une procédure stockée à la place: P>
create procedure dbo.NukeTabData( @idData int) as begin transaction update tabData set fiData = null where fiData = @idData delete from tabData where idData = @idData commit transaction go
Maintenant, votre code d'appel est EXEC NUKETABDATA (ID) CODE>, et vous n'avez aucune idée de ce que cela ne fait pas examiner le contenu de la procédure. Pourquoi ça va, quand un déclencheur n'est pas?
@Damien_the_unbeliever: La procédure stockée est explicite: elle me dit où je devrais regarder si je suis intéressé par les détails. Un déclencheur est implicite: rien dans Supprimer de Tabdata où iddata = 42 code> Conseils en présence de mises à jour supplémentaires qui seront exécutées. Par exemple, après avoir exécuté la suppression, un déclencheur pourrait causer
@@ rowcount code> à 4 au lieu de 1. Il en résulte des bugs difficiles - en particulier pour les développeurs qui sont nouveaux à la base de code.
@@ RowCount est basé sur la portée - elle n'est affectée par aucune activité dans la gâchette. Vous pensez éventuellement à @@ identité, mais la plupart des gens savent éviter que ces jours-ci
@Damien_the_unbeliever: Eh bien, il y a toute une classe d'effets secondaires: @@ identité code>, possibilité de restauration, des blocages inattendus. Une erreur commune semble être des audits ou une journalisation des erreurs à l'aide d'un déclencheur. Lorsque la transaction est renvoyée, la journalisation est renvoyée également. Ainsi, lorsqu'un nouvel ordre échoue et est renvoyé, il n'apparaît pas dans les journaux d'erreur et on dirait qu'aucune commande n'a été placée du tout.
@Andomar: merci. Mais le tableau est déjà impliqué dans plusieurs applications et services dans lesquels chacun pourrait changer / supprimer des enregistrements. Cela n'aurait pas d'intérêt à changer tout à l'aide de la procédure stockée à la place.
@Andomar: J'ai fini par utiliser ce que j'ai déjà mentionné, "appliquer FK-CONTRAINT = NO" et A après avoir supprimé code> -Trigger qui définit Fidata à NULL. Pouvez-vous expliquer ce que vous voulez dire avec "l'optimiseur de requête sera moins efficace car il ne peut pas supposer que la clé est valide"? La table a déjà ~ 12 millions de lignes et est interrogée franchement.
Fidata Code> ne sera jamais ajouté manuellement mais calculé par un processus stocké tous les matins. Par conséquent, I B> peut vous assurer que le FK est valide. Comment dire que SQL-Server?
@Tim Schmelter: Une clé étrangère est la seule manière efficace, mais cela ne devrait pas trop comprendre, généralement une micro-optimisation.
Contrairement à l'Andomar, je serais heureux d'utiliser un déclencheur - mais je ne supprimerais pas la vérification des contraintes. Si vous l'implémentez comme un au lieu de la gâchette CODE>, vous pouvez réinitialiser les autres lignes à NULL avant d'effectuer la suppression réelle:
CREATE TRIGGER T_tabData_D
on tabData
instead of delete
as
set nocount on
update tabData set fiData = null where fiData in (select idData from deleted)
delete from tabData where idData in (select idData from deleted)
Merci. Mais je reçois une exception sur la création de la gâchette: "Au lieu de supprimer / mettre à jour les déclencheurs ne peuvent pas être définis sur une table présentant une clé étrangère avec une cascade sur une action de suppression / mise à jour définie".
Je le sais trop tard pour commenter mais pour quelqu'un qui cherche comme moi. Cela ne supprimera qu'une seule entrée. Il ne cassera pas de manière récursive.
@Arif - C'était "sur Suppriming Set Null", pas "Sur Suppr Cascade". Le premier n'a jamais besoin de recueillir. Pour "On Suppr Cascade", je recommanderais un CTE qui calcule la fermeture sur toutes les valeurs code> id code> d'abord, puis effectue la suppression.
Je n'ai pas remarqué cela en question. Mais le premier commentaire dit "Cascade sur Supprimer / update" commenté par la personne qui s'est interrogée.
@ Timschmelter j'ai été bloqué au même point. Je pense que la solution pourrait être de retirer les autres actions en cascade de la table, puis d'ajouter la logique équivalente dans la gâchette ... Je n'ai toujours pas trouvé de meilleure façon de le faire. S'il vous plaît dire au cas où vous en trouveriez un
cela très tard pour répondre.
Mais pour quelqu'un qui cherche comme moi. p>
et veulent ici est très Bonne explication p> http: / /devio.wordpress.com/2008/05/23/Recursive-Delete-in-sql-server/ P> Le problème
Bien que vous puissiez définir une clé étrangère avec Cascade Supprez dans SQL Server, les suppressions en cascade récursives ne sont pas prises en charge (c.-à-d. Supprimer en cascade sur la même table). P> Si vous créez un déclencheur, ce déclencheur ne fait que se déclencher. Pour la première instruction DELETE, et ne déclenche pas pour des enregistrements supprimés de ce déclencheur. p> Ce comportement est documenté sur MSDN pour SQL Server 2000 et SQL Server 2005. P> La solution
Supposons que vous ayez une table définie comme ceci: p> puis le déclencheur de suppression ressemble à ceci: p> cascade code> p>
CREATE TRIGGER del_MyTable ON MyTable INSTEAD OF DELETE
AS
CREATE TABLE #Table(
OID INT
)
INSERT INTO #Table (OID)
SELECT OID
FROM deleted
DECLARE @c INT
SET @c = 0
WHILE @c <> (SELECT COUNT(OID) FROM #Table) BEGIN
SELECT @c = COUNT(OID) FROM #Table
INSERT INTO #Table (OID)
SELECT MyTable.OID
FROM MyTable
LEFT OUTER JOIN #Table ON MyTable.OID = #Table.OID
WHERE MyTable.OID_Parent IN (SELECT OID FROM #Table)
AND #Table.OID IS NULL
END
DELETE MyTable
FROM MyTable
INNER JOIN #Table ON MyTable.OID = #Table.OID
GO
C'est une bonne réponse à la suppression de la cascade, mais la question concerne la touche DELETE SET NULL