Partilhar via


Criar Gatilhos Aninhados

Os gatilhos DML e DDL ficam aninhados quando um gatilho executa uma ação que inicia outro gatilho. Essas ações podem iniciar outros gatilhos e assim por diante. Gatilhos DML e DDL podem ser aninhados até 32 níveis. Você pode controlar se os gatilhos AFTER podem ser aninhados por meio da opção de configuração de servidor nested triggers. Gatilhos INSTEAD OF (somente gatilhos DML podem ser gatilhos INSTEAD OF) podem ser aninhados independentemente dessa configuração.

Observação

Qualquer referência ao código gerenciado a partir de um gatilho Transact-SQL é contabilizada como um nível em relação ao limite de aninhamento que é de 32 níveis. Os métodos invocados de dentro do código gerenciado não contam com esse limite.

Se gatilhos aninhados são permitidos e um gatilho na cadeia inicia um loop infinito, o nível de aninhamento é excedido e o gatilho termina.

Você pode usar gatilhos aninhados para executar funções úteis de limpeza, como armazenar uma cópia de backup de linhas afetadas por um gatilho anterior. Por exemplo, você pode criar um gatilho em PurchaseOrderDetail que salva uma cópia de backup das linhas PurchaseOrderDetail que o gatilho delcascadetrig excluiu. Com o gatilho delcascadetrig em efeito, excluir PurchaseOrderID 1965 de PurchaseOrderHeader exclui a linha ou as linhas correspondentes de PurchaseOrderDetail. Para salvar os dados, você pode criar um gatilho DELETE em PurchaseOrderDetail que salva os dados excluídos em uma outra tabela, criada separadamente, del_save. Por exemplo:

CREATE TRIGGER Purchasing.savedel  
   ON Purchasing.PurchaseOrderDetail  
FOR DELETE  
AS  
   INSERT del_save;  
   SELECT * FROM deleted;  

Não recomendamos usar gatilhos aninhados em uma sequência dependente de ordem. Use gatilhos separados para fazer modificações de dados em cascata.

Observação

Como os gatilhos são executados dentro de uma transação, uma falha em qualquer nível de um conjunto de gatilhos aninhados cancela toda a transação e todas as modificações de dados são revertidas. Inclua declarações PRINT em seus gatilhos para que você possa determinar onde a falha ocorreu.

Gatilhos recursivos

Um gatilho AFTER não se chama recursivamente, a menos que a opção de banco de dados RECURSIVE_TRIGGERS esteja definida.

Há dois tipos de recursão:

  • Recursão direta

    Essa recursão ocorre quando um gatilho é acionado e executa uma ação que faz com que o mesmo gatilho seja acionado novamente. Por exemplo, um aplicativo atualiza a tabela T3; isso faz com que o gatilho Trig3 seja acionado. Trig3 atualiza a tabela T3 novamente; isso faz com que o gatilho Trig3 seja acionado novamente.

    A recursão direta também pode ocorrer quando o mesmo gatilho é chamado novamente, mas depois que um gatilho de um tipo diferente (AFTER ou INSTEAD OF) é chamado. Em outras palavras, a recursão direta de um gatilho INSTEAD OF pode ocorrer quando o mesmo gatilho INSTEAD OF é chamado pela segunda vez, mesmo que um ou mais gatilhos AFTER sejam chamados no meio. Da mesma forma, a recursão direta de um gatilho AFTER pode ocorrer quando o mesmo gatilho AFTER é chamado pela segunda vez, mesmo que um ou mais gatilhos INSTEAD OF sejam chamados no meio. Por exemplo, um aplicativo atualiza a tabela T4. Essa atualização faz com que o gatilho INSTEAD OF Trig4 seja acionado. Trig4 atualiza a tabela T5. Essa atualização faz com que o disparador AFTER Trig5 seja acionado. Trig5 atualiza a tabela T4, e essa atualização faz com que o gatilho INSTEAD OF Trig4 seja acionado novamente. Essa cadeia de eventos é considerada recursão direta para Trig4.

  • Recursão indireta

    Essa recursão ocorre quando um gatilho é acionado e executa uma ação que faz com que outro gatilho do mesmo tipo (AFTER ou INSTEAD OF) seja acionado. Este segundo gatilho executa uma ação que faz com que o gatilho original seja acionado novamente. Em outras palavras, a recursão indireta pode ocorrer quando um gatilho INSTEAD OF é chamado pela segunda vez, mas não até que outro gatilho INSTEAD OF seja chamado no meio. Da mesma forma, a recursão indireta pode ocorrer quando um gatilho AFTER é chamado pela segunda vez, mas não até que outro gatilho AFTER seja chamado no meio. Por exemplo, um aplicativo atualiza a tabela T1. Essa atualização faz com que o gatilho AFTER Trig1 seja acionado. Trig1 atualiza a tabela T2 e essa atualização faz com que o gatilho do tipo AFTER Trig2 seja acionado. Trig2, por sua vez, atualiza a tabela T1, causando o acionamento do gatilho AFTER Trig1 novamente.

Somente a recursão direta de gatilhos AFTER é impedida quando a opção de banco de dados RECURSIVE_TRIGGERS está definida como OFF. Para desabilitar a recursão indireta dos gatilhos AFTER, defina também a opção de servidor de gatilhos aninhados como 0.

Exemplos

O exemplo a seguir mostra o uso de gatilhos recursivos para resolver uma relação de auto-referência (também conhecida como fechamento transitivo). Por exemplo, a tabela emp_mgr define o seguinte:

  • Um funcionário (emp) em uma empresa.

  • O gerente de cada funcionário (mgr).

  • O número total de funcionários na árvore organizacional relacionados a cada funcionário (NoOfReports).

Um gatilho de atualização recursivo pode ser usado para manter a coluna NoOfReports up-to-date enquanto novos registros de funcionário são inseridos. O gatilho INSERT atualiza a NoOfReports coluna do registro do gerente, que atualiza recursivamente a NoOfReports coluna de outros registros na hierarquia de gerenciamento.

USE AdventureWorks2012;  
GO  
-- Turn recursive triggers ON in the database.  
ALTER DATABASE AdventureWorks2012  
   SET RECURSIVE_TRIGGERS ON;  
GO  
CREATE TABLE dbo.emp_mgr (  
   emp char(30) PRIMARY KEY,  
    mgr char(30) NULL FOREIGN KEY REFERENCES emp_mgr(emp),  
    NoOfReports int DEFAULT 0  
);  
GO  
CREATE TRIGGER dbo.emp_mgrins ON dbo.emp_mgr  
FOR INSERT  
AS  
DECLARE @e char(30), @m char(30);  
DECLARE c1 CURSOR FOR  
   SELECT emp_mgr.emp  
   FROM   emp_mgr, inserted  
   WHERE emp_mgr.emp = inserted.mgr;  
  
OPEN c1;  
FETCH NEXT FROM c1 INTO @e;  
WHILE @@fetch_status = 0  
BEGIN  
   UPDATE dbo.emp_mgr  
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Add 1 for newly  
   WHERE emp_mgr.emp = @e ;                           -- added employee.  
  
   FETCH NEXT FROM c1 INTO @e;  
END  
CLOSE c1;  
DEALLOCATE c1;  
GO  
-- This recursive UPDATE trigger works assuming:  
--   1. Only singleton updates on emp_mgr.  
--   2. No inserts in the middle of the org tree.  
CREATE TRIGGER dbo.emp_mgrupd ON dbo.emp_mgr FOR UPDATE  
AS  
IF UPDATE (mgr)  
BEGIN  
   UPDATE dbo.emp_mgr  
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Increment mgr's  
   FROM inserted                            -- (no. of reports) by  
   WHERE emp_mgr.emp = inserted.mgr;         -- 1 for the new report.  
  
   UPDATE dbo.emp_mgr  
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports - 1 -- Decrement mgr's  
   FROM deleted                             -- (no. of reports) by 1  
   WHERE emp_mgr.emp = deleted.mgr;          -- for the new report.  
END  
GO  
-- Insert some test data rows.  
INSERT dbo.emp_mgr(emp, mgr) VALUES  
    ('Harry', NULL)  
    ,('Alice', 'Harry')  
    ,('Paul', 'Alice')  
    ,('Joe', 'Alice')  
    ,('Dave', 'Joe');  
GO  
SELECT emp,mgr,NoOfReports  
FROM dbo.emp_mgr;  
GO  
-- Change Dave's manager from Joe to Harry  
UPDATE dbo.emp_mgr SET mgr = 'Harry'  
WHERE emp = 'Dave';  
GO  
SELECT emp,mgr,NoOfReports FROM emp_mgr;  
  
GO  

Aqui estão os resultados antes da atualização.

emp                            mgr                           NoOfReports  
------------------------------ ----------------------------- -----------  
Alice                          Harry                          2  
Dave                           Joe                            0  
Harry                          NULL                           1  
Joe                            Alice                          1  
Paul                           Alice                          0  

Aqui estão os resultados após a atualização.

emp                            mgr                           NoOfReports  
------------------------------ ----------------------------- -----------  
Alice                          Harry                          2  
Dave                           Harry                          0  
Harry                          NULL                           2  
Joe                            Alice                          0  
Paul                           Alice                          0  

Para definir a opção de gatilhos aninhados

Para definir a opção de banco de dados RECURSIVE_TRIGGERS

Consulte Também

CREATE TRIGGER (Transact-SQL)
Configurar a opção de configuração de gatilhos aninhados do servidor