Question

Je voudrais savoir s'il y a de toute façon je peux ajouter un déclencheur sur deux tables qui répliquer les données à l'autre.

Par exemple:

  • J'ai deux tables utilisateurs, users_V1 et users_V2, Lorsqu'un utilisateur est mis à jour avec une des applications V1, il active un déclencheur de mise à jour dans users_V2 ainsi.

  • Si je veux ajouter le même déclencheur sur la table V2 afin de mettre à jour les données V1 lorsqu'un utilisateur est mis à jour en V2, sera-il aller dans une boucle infinie? Est-il possible d'éviter cela.

Était-ce utile?

La solution

Je ne recommande pas de désactiver explicitement la gâchette pendant le traitement - cela peut provoquer d'étranges effets secondaires

.

La façon la plus fiable pour détecter (et prévenir) les cycles dans un déclencheur est d'utiliser CONTEXT_INFO().

Exemple:

CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS

DECLARE @ctx VARBINARY(128) 
SELECT @ctx = CONTEXT_INFO() 
IF @ctx = 0xFF
    RETURN

SET @ctx = 0xFF

-- Trigger logic goes here

Voir ce lien pour un exemple plus détaillé.


Remarque sur CONTEXT_INFO() dans SQL Server 2000:

info Le contexte est pris en charge, mais apparemment la fonction CONTEXT_INFO n'est pas. Vous devez utiliser ceci:

SELECT @ctx = context_info
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

Autres conseils

  • Soit utiliser TRIGGER_NESTLEVEL () pour restreindre la récursivité de déclenchement ou

  • vérifier la table cible si une mise à jour est nécessaire à tous:

    IF (SELECT COUNT(1) 
    FROM users_V1 
    INNER JOIN inserted ON users_V1.ID = inserted.ID
    WHERE users_V1.field1 <> inserted.field1
    OR users_V1.field2 <> inserted.field2) > 0 BEGIN
    
    UPDATE users_V1 SET ...
    

J'ai eu exactement le même problème. J'ai essayé d'utiliser CONTEXT_INFO () mais qui est une variable de session et il ne fonctionne que la première fois! Alors la prochaine fois un feux de déclenchement au cours de la session, cela ne fonctionnera pas. Donc, je me suis retrouvé avec l'aide d'une variable qui retourne Niveau Nest dans chacun des déclencheurs concernés pour quitter.

Exemple:

CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS
BEGIN
      --Prevents Second Nested Call
      IF @@NESTLEVEL>1 RETURN 

      --Trigger logic goes here
END

Remarque: Ou utilisez @@ NESTLEVEL> 0 si vous voulez arrêter tous les appels imbriqués

Une autre note - Il semble y avoir beaucoup de confusion dans cet article sur les appels imbriqués et les appels récursifs. L'affiche originale faisait référence à un déclencheur imbriqué où un déclencheur provoquerait un autre déclencheur au feu, ce qui provoquerait le premier déclencheur à nouveau le feu, et ainsi de suite. C'est emboîtée, mais selon SQL Server, et non récursif parce que le déclencheur ne demande pas / se déclenche directement. Récursion est pas où « un déclencheur [est] d'appeler un autre ». Imbriqué, mais pas nécessairement récursive. Vous pouvez tester cela en activer / désactiver la récursivité et l'imbrication avec certains paramètres mentionnés ici: blog sur l'imbrication

Je suis avec le camp pour ne déclenche ce scénario de conception particulière. Cela dit, les connaissances dont je dispose à ce que votre application fait et pourquoi elle le fait, voici mon analyse globale:

L'utilisation d'un déclencheur sur une table a l'avantage d'être en mesure d'agir sur toutes les actions sur la table. Ca y est, votre principal avantage dans ce cas. Mais cela voudrait dire que vous avez des utilisateurs avec un accès direct aux points d'accès multiples tables ou à la table. Je tends à éviter cela. Triggers ont leur place (je les utilise beaucoup), mais il est l'un des derniers outils de conception de base de données que j'utilise parce qu'ils ont tendance à connaître pas beaucoup de leur contexte (en général, une force) et lorsqu'il est utilisé dans un endroit où ils ont besoin de connaître les différents contextes et l'ensemble des cas d'utilisation, leurs avantages sont affaiblis.

Si les deux versions d'applications doivent déclencher la même action, ils doivent à la fois appeler le même proc stocké. La procédure stockée peut faire en sorte que tout le travail approprié est fait, et quand votre application n'a plus besoin de soutenir V1, peut alors être retiré cette partie de la procédure stockée.

Appeler deux procs stockés dans votre code client est une mauvaise idée, car cela est une couche d'abstraction des services de données qui la base de données peut fournir facilement et de manière cohérente, sans que votre demande soit inquiet à ce sujet.

Je préfère contrôler l'interface avec les tables sous-jacentes plus - avec soit des vues ou des FDU ou SPs. Les utilisateurs ne sont jamais accès direct à une table. Un autre point ici est que vous pouvez présenter un « utilisateurs » VIEW ou UDF coalescent les tables sous-jacentes appropriées sans que l'utilisateur même savoir au sujet - peut-être obtenir au point où il n'y a même pas de « synchronisation » nécessaire, puisque les nouveaux attributs sont dans une système EAV si vous avez besoin de ce genre de flexibilité pathologique ou d'une autre structure différente qui peut encore être joint -. dire OUTER APPLY UDF etc

Vous allez devoir créer une sorte de détection de bouclage au sein de votre déclencheur. Peut-être en utilisant une déclaration « si elle existe » pour voir si l'enregistrement existe avant d'entrer dans le tableau suivant. Il ne semble que cela va aller dans une boucle infinie la façon dont il est actuellement mis en place.

Évitez les déclencheurs comme la peste .... utiliser une procédure stockée pour ajouter l'utilisateur. Si cela nécessite des changements de conception puis de les faire. Les déclencheurs sont le EVIL.

Essayez quelque chose comme (je n; t embêter avec thecreate trucs déclencheur comme vous le savez déjà clairement comment écrire cette partie):

update t
set field1 = i.field1
field2 = i.field2
from inserted i
join table1 t on i.id  = t.id
where field1 <> i.field1 OR field2 <> i.field2

récursivité dans les déclencheurs, c'est un déclencheur d'appeler un autre, est limité à 32 niveaux

Dans chaque déclencheur, il suffit de vérifier si la ligne que vous souhaitez insérer existe déjà.

Exemple

CREATE TRIGGER Table1_Synchronize_Update ON [Table1] FOR UPDATE AS
BEGIN
  UPDATE  Table2
  SET     LastName = i.LastName
          , FirstName = i.FirstName
          ,  ... -- Every relevant field that needs to stay in sync
  FROM    Table2 t2
          INNER JOIN Inserted i ON i.UserID = t2.UserID
  WHERE   i.LastName <> t2.LastName
          OR i.FirstName <> t2.FirstName
          OR ... -- Every relevant field that needs to stay in sync
END

CREATE TRIGGER Table1_Synchronize_Insert ON [Table1] FOR INSERT AS
BEGIN
  INSERT INTO Table2
  SELECT i.*
  FROM   Inserted i
         LEFT OUTER JOIN Table2 t2 ON t2.UserID = i.UserID
  WHERE  t2.UserID IS NULL
END

CREATE TRIGGER Table2_Synchronize_Update ON [Table2] FOR UPDATE AS
BEGIN
  UPDATE  Table1
  SET     LastName = i.LastName
          , FirstName = i.FirstName
          ,  ... -- Every relevant field that needs to stay in sync
  FROM    Table1 t1
          INNER JOIN Inserted i ON i.UserID = t1.UserID
  WHERE   i.LastName <> t1.LastName
          OR i.FirstName <> t1.FirstName
          OR ... -- Every relevant field that needs to stay in sync
END

CREATE TRIGGER Table2_Synchronize_Insert ON [Table2] FOR INSERT AS
BEGIN
  INSERT INTO Table1
  SELECT i.*
  FROM   Inserted i
         LEFT OUTER JOIN Table1 t1 ON t1.UserID = i.UserID
  WHERE  t1.UserID IS NULL
END
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top