2009-10-07 10 views
21

Mam następujący wyzwalacz w tabela dla bazy danych SQL Server 2008. Powtarza się, więc muszę to zatrzymać.Jak zapobiec powtarzaniu wyzwalacza bazy danych?

Po wstawieniu lub aktualizacji rekordu próbuję po prostu zaktualizować pojedyncze pole w tej tabeli.

Oto spust:

ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate] 
    ON [dbo].[tblMedia] 
    BEFORE INSERT, UPDATE 
AS 
BEGIN 
    SET NOCOUNT ON 

    DECLARE @IdMedia INTEGER, 
     @NewSubject NVARCHAR(200) 

    SELECT @IdMedia = IdMedia, @NewSubject = Title 
    FROM INSERTED 

    -- Now update the unique subject field. 
    -- NOTE: dbo.CreateUniqueSubject is my own function. 
    --  It just does some string manipulation. 
    UPDATE tblMedia 
    SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
         CAST((IdMedia) AS VARCHAR(10)) 
    WHERE tblMedia.IdMedia = @IdMedia 
END 

Czy ktoś może mi powiedzieć jak mogę zapobiec wkładkę spust jest od znowu rozpoczynamy kolejny spust?

+0

Wiele ludzi mówi, aby wyłączyć rekursji wyzwalania. teraz, nie będę chciał dotknąć tego ustawienia. Wolę naprawić tsql. –

+0

Następnie peryferyjne spust nie powinien być PRZED, ale wyzwalacz INSTEAD OF? http://msdn.microsoft.com/en-us/library/ms175089.aspx –

Odpowiedz

1

Możesz mieć oddzielną kolumnę NULLABLE wskazującą, czy został ustawiony zestaw UniqueTitle.

Ustaw go do prawdziwej wartości w wyzwalacz, i spust zrobić nic, jeśli jego wartość jest prawdziwe w „włożonej”

+0

Dlaczego głosowanie oddano w dół? – DVK

30

Widzę trzy możliwości:

  1. Wyłącz rekurencji spust:

    Zapobiegnie to uruchomieniu wyzwalacza w celu wywołania innego wyzwalacza lub ponownego wywołania siebie. Aby to zrobić, należy wykonać polecenie:

    ALTER DATABASE MyDataBase SET RECURSIVE_TRIGGERS OFF 
    GO 
    
  2. użyć wyzwalacza ZAMIAST UPDATE, INSERT

    Używanie INSTEAD OF wyzwalania można sterować każdą kolumnę aktualizowany/włożona, a nawet zastąpienie przed wywołaniem dowództwo.

  3. Kontrola spust uniemożliwiając korzystania IF UPDATE

    Testowanie kolumna powie z należytą dokładnością, jeśli dzwoni się wyzwolić. W tym celu użyć klauzuli IF UPDATE() jak:

    ALTER TRIGGER [dbo].[tblMediaAfterInsertOrUpdate] 
        ON [dbo].[tblMedia] 
        FOR INSERT, UPDATE 
    AS 
    BEGIN 
        SET NOCOUNT ON 
        DECLARE @IdMedia INTEGER, 
         @NewSubject NVARCHAR(200) 
    
        IF UPDATE(UniqueTitle) 
         RETURN; 
    
        -- What is the new subject being inserted? 
        SELECT @IdMedia = IdMedia, @NewSubject = Title 
        FROM INSERTED 
    
        -- Now update the unique subject field. 
        -- NOTE: dbo.CreateUniqueSubject is my own function. 
        --  It just does some string manipulation. 
        UPDATE tblMedia 
        SET UniqueTitle = dbo.CreateUniqueSubject(@NewSubject) + 
             CAST((IdMedia) AS VARCHAR(10)) 
        WHERE tblMedia.IdMedia = @IdMedia 
    END 
    
+0

Szybkie pytanie. Używasz _BEFORE_ zamiast _AFTER_. Czy to nadal da mi nową wartość Włożonego (Identity) ID? A może to tylko powstaje na _AFTER_? –

+0

Naprawiono. Wystrzelić spust po wstawieniu/aktualizacji – Rodrigo

+0

Nie - nie działa. Robię wstawkę, ale aktualizacja (UniqueTitle) musi być uważana za aktualizację ... ??? –

7
ALTER DATABASE <dbname> SET RECURSIVE_TRIGGERS OFF 

RECURSIVE_TRIGGERS { ON | OFF }

NA rekurencyjnych wypalania PO wyzwala jest dozwolone.

OFF Tylko bezpośrednie wyzwalanie cykliczne wyzwalaczy AFTER jest niedozwolone. Aby wyłączyć również pośrednią rekursję wyzwalaczy AFTER z , ustaw zagnieżdżoną opcję uruchamiającą serwer na 0, używając sp_configure.

Bezpośrednie rekursje są wyłączone, gdy RECURSIVE_TRIGGERS jest ustawione na WYŁ. Aby wyłączyć pośrednią rekursję, musisz ustawić opcję serwera zagnieżdżonych dla opcji zagnieżdżonych na 0.

Stan tej opcji można określić badając kolumny is_recursive_triggers_on w sys.databases widoku katalogu lub IsRecursiveTriggersEnabled własność funkcja DATABASEPROPERTYEX się.

5

myślę mam go :)

Gdy tytuł jest uzyskiwanie „zaktualizowane” (czytaj: wstawione lub zaktualizowane), a następnie zaktualizować unikalny przedmiot. Gdy wyzwalacz zostanie uruchomiony po raz drugi, pole uniquesforme zostaje zaktualizowane, więc zatrzymuje się i pozostawia wyzwalacz.

Co więcej, zmusiłem go do obsłużenia WIELU rzędów, które się zmieniły -> Zawsze zapominam o tym z wyzwalaczami.

ALTER TRIGGER [dbo].[tblMediaAfterInsert] 
    ON [dbo].[tblMedia] 
    FOR INSERT, UPDATE 
AS 
BEGIN 
    SET NOCOUNT ON 

    -- If the Title is getting inserted OR updated then update the unique subject. 
    IF UPDATE(Title) BEGIN 
     -- Now update all the unique subject fields that have been inserted or updated. 
     UPDATE tblMedia 
     SET UniqueTitle = dbo.CreateUniqueSubject(b.Title) + 
          CAST((b.IdMedia) AS VARCHAR(10)) 
     FROM tblMedia a 
      INNER JOIN INSERTED b on a.IdMedia = b.IdMedia 
    END 
END 
+0

Nie jestem pewien, dlaczego zostało to odrzucone przez każdego, jest to całkiem pomocne rozwiązanie. –

0

Dla kompletności dodam kilka rzeczy. Jeśli masz specjalny wyzwalacz po uruchomieniu, który chcesz uruchomić tylko raz, możesz ustawić go tak, aby działał jako ostatni za pomocą sp_settriggerorder.

Zastanowiłabym się również, czy nie najlepiej jest połączyć wyzwalacze, które wykonują rekursję w jeden wyzwalacz.

41

Nie wiem, czy to jest istotne na pytanie OP już, ale w przypadku, gdy przyszedł tutaj, aby dowiedzieć się, jak zapobiec rekursji lub wzajemnej rekursji dzieje w wyzwalacz, można przetestować na to tak:

IF TRIGGER_NESTLEVEL() <= 1/*this update is not coming from some other trigger*/ 

MSDN link

+5

To jest właściwie najlepsza odpowiedź i ta, która najbardziej bezpośrednio odpowiada na pytanie z plakatu. – Curt

+0

tak, to powinna być odpowiedź. dzięki –

+1

Tak jak odpowiada odpowiedź, zapobiega to również uruchomieniu wyzwalacza, jeśli aktualizacja pochodzi z innego wyzwalacza. Ale to nie jest rekursja. Rekurencja występuje, gdy aktualizacja ma taki sam mechanizm uruchamiający. W takim przypadku musisz przekazać identyfikator obiektu do funkcji: https://stackoverflow.com/a/47074365/150342 – Colin

1

TRIGGER_NESTLEVEL może być stosowany w celu zapobiegania rekursji specyficznego wyzwalania, ale ważne jest, aby przekazać identyfikator obiektu spustu w funkcji. W przeciwnym razie będzie również zapobiec spust z odpaleniem gdy wkładka lub zmiana wykonana przez innego wyzwalania:

IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.mytrigger')) > 1 
     BEGIN 
      PRINT 'mytrigger exiting because TRIGGER_NESTLEVEL > 1 '; 
      RETURN; 
    END; 

Od MSDN:

Jeżeli nie podano żadnych parametrów, TRIGGER_NESTLEVEL Zwraca całkowitą liczbę wyzwalaczy na stosie wywołań. To obejmuje samą siebie.

referencyjny: Avoiding recursive triggers

Powiązane problemy