04_triggers

Propaganda
Linguagem de Programação de
Banco de Dados
George Azevedo da Silva
José Antônio da Cunha
IFRN
Gatilhos
• É um tipo especial de Stored Procedure, que
executa tarefas de manipulação de dados
dentro de um banco de dados. Os gatilhos,
são executados indiretamente, pelos
comandos INSERT e/ou UPDATE e/ou DELETE.
Gatilhos
Usuário Faz
Uma venda
“INSERT”
Tabela ItensVendidos
Qtd_Prod
Gatilho de
Baixa de Estoque
Figura 1
UPDATE
Tabela Produtos
Coluna Qtd_Estq
Gatilhos
• Os gatilhos não recebem parâmetros de
entrada, sendo assim, para que seja possível
manipular, dentro do gatilho, os dados que
estão sendo afetados pelo comando que
acionou o gatilho, existem as tabelas
chamadas INSERTED e DELETED.
Tabelas INSERTED e DELETED
• Essas tabelas são criadas na memória em
tempo de execução do gatilho e você só pode
realizar o comando SELECT sobre elas.
Tabelas INSERTED e DELETED
• Os três tipos de gatilhos têm as duas tabelas, mas
nos gatilhos de Insert apenas a tabela inserted terá
dados. Nos gatilhos delete apenas deleted terá
dados. Nos gatilhos de update as duas tabelas
inserted e deleted terão dados – a tabela deleted
terá os dados da forma como eles eram antes de o
update ser feito e a tabela inserted terá os dados
novos já modificados.
Gatilhos
• As tabelas inserted e deleted terão a mesma
estrutura da tabela base do gatilho, e o dados
afetados pelo comando que aciona os
gatilhos.
Gatilhos
Gatilho de Insert
Gatilho de Delete
Gatilho de Update
A tabela inserted terá dados
novos.
A tabela deleted terá dados que
foram deletados.
A tabela deleted terá os dados
antigos e a tabela inserted terá
os dados novos.
Gatilhos
• Sintaxe
CREATE TRIGGER Nome_Gatilho
ON tabela_base
FOR INSERT/UPDATE/DELETE
AS
Corpo da gatilho
Exemplo de Gatilhos
• Suponha clientes comprando produtos por meio de
pedidos. Esses clientes podem fazer vários pedidos e
em cada pedido que fazem podem comprar muitos
produtos. Você realiza a venda de cada produto,
registrando os dados na tabela ItensVendidos. A
cada item vendido, seu sistema deve realizar a baixa
da quantidade vendida desse produto no estoque.
Para fazer esta baixa de estoque, você pode utilizar
um gatilho.
Gatilhos
Vamos acrescentar no banco dados de exemplo as seguintes tabelas:
CREATE TABLE ItensVendidos
CREATE TABLE Produto
(
(
Num_Ped int,
Cod_Prod int,
Cod_Prod int,
Nome_Prod varchar(50),
Qtd_Prod float,
Qtd_Est float
Data_Ped datetime
)
)
Gatilhos
Vamos popular a tabela de produtos com dados abaixo
Cod_Prod
Nome_Prod
Qtd_est
1
Refrigerante Guarana Lata 350 ml
500,00
2
Refrigerante Cola Lata 350 ml
600,00
3
Refrigerante Guarana Pet 1000 ml
200,00
4
Refrigerante Cola Pet 1000 ml
250,00
5
Refrigerante Guarana Pet 2000 ml
400,00
6
Refrigerante Cola Pet 2000 ml
450,00
Gatilhos
Comandos para popular a tabela de produtos
insert
values
insert
values
insert
values
insert
values
insert
values
insert
values
into
(1,
into
(2,
into
(3,
into
(4,
into
(5,
into
(6,
Produto
'Refrigerante
Produto
'Refrigerante
Produto
'Refrigerante
Produto
'Refrigerante
Produto
'Refrigerante
Produto
'Refrigerante
Guarana Lata 350 ml',500)
Cola Lata 350 ml',600)
Guarana Pet 1000 ml',200)
Cola Pet 1000 ml',250)
Guarana Pet 2000 ml',400)
Cola Pet 2000 ml',450)
CREATE TRIGGER T_Baixa
Tabela Base do Gatilho
ON ItensVendidos
FOR INSERT
AS
UPDATE Produto
SET Produto.Qtd_Est = Produto.Qtd_Est - Inserted.Qtd_Prod
FROM Inserted
Where Produto.Cod_Prod = inserted.Cod_Prod
IF @@ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
RAISERROR('Erro de Processamento – Chame o Adminitrador', 16,1)
RETURN
END
Gatilhos
Para disparar o gatilho anterior, execute o seguinte comando:
INSERT itensVendidos (Num_Ped, Cod_Prod, Qtd_Prod,
data_ped) VALUES (1,3,5,getdate()-10)
INSERT itensVendidos (Num_Ped, Cod_Prod, Qtd_Prod,
data_ped) VALUES (2,3,5,getdate()-1)
Estes comandos realizam uma venda de 5 unidades do produto de
código 3 para o pedido de número 1 com data de dez dias atrás e
uma venda de 5 unidades do produto de código 3 para o pedido de
número 2 com data de 1 dia atrás.
Exemplo 2 - Gatilhos
Suponha que nessa empresa você aceita que seus clientes devolvam
os produtos que foram comprados até 5 dias corridos atrás. Esta
devolução deve ser refletida no estoque. Você deve recolocar esse
produto no estoque e disponibilizá-lo novamente para venda,
acrescentando-o ao estoque. Para tanto você poderia utilizar o
gatilho a seguir:
CREATE TRIGGER T_Devolucao
ON ItensVendidos
FOR DELETE
AS
declare @data datetime
set @data = (select min(Data_Ped) from deleted)
IF (DateDiff(dd, @data, GetDate())) <= 5
BEGIN
UPDATE Produto
SET Produto.Qtd_Est = Produto.Qtd_Est + deleted.Qtd_Prod
FROM deleted
WHERE Produto.Cod_Prod = deleted.Cod_Prod
IF @@ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
RAISERROR('Erro de Processamento – Chame o Administrador', 16,1)
RETURN
END
END
ELSE
BEGIN
RAISERROR('Prazo de Devolução vencido – Operação rejeitada',16,1)
ROLLBACK TRANSACTION
RETURN
END
Gatilhos
Observe que este é um tigger de deleção (FOR DELETE) e quando
uma linha for excluída da tabela ItensVendidos, o gatilho será
acionado e fará um Update na tabela Produto, acrescentando a
quantidade que havia sido vendida de volta ao estoque, caso a data
do pedido seja de até 5 dias. Caso contrário a tentativa exclusão é
descartada
Os comandos seguintes acionam a execução do gatilho anterior:
DELETE ItensVendidos Where Num_Ped = 1
DELETE ItensVendidos Where Num_Ped = 2
Exemplo 3 - Gatilhos
A empresa poderia também, num mesmo prazo de 5 dias, aceitar
troca de produtos para o mesmo pedido. Para tanto, você deve
escrever um gatilho que recolocasse no estoque o produto a ser
devolvido e retirasse do estoque o produto que será levado pelo
cliente. Veja a listagem a seguir:
Gatilhos
CREATE TRIGGER T_Troca
ON ItensVendidos
FOR UPDATE
AS
declare @data datetime
set @data = (select min(Data_Ped) from deleted)
IF (DateDiff(dd, @data, GetDate())) <= 5
BEGIN
-- Baixa produtos inseridos
UPDATE Produto
SET Produto.Qtd_Est = Produto.Qtd_Est - Inserted.Qtd_Prod
FROM Inserted
Where Produto.Cod_Prod = inserted.Cod_Prod
-- Acrescenta produtos retirados
UPDATE Produto
SET Produto.Qtd_Est = Produto.Qtd_Est + deleted.Qtd_Prod
FROM deleted
WHERE Produto.Cod_Prod = deleted.Cod_Prod
Gatilhos
IF @@ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
RAISERROR('Erro de Processamento – Chame o ADM',16,1)
RETURN
END
END
ELSE
BEGIN
RAISERROR('Prazo de troca vencido – Operação rejeitada',16,1)
ROLLBACK TRANSACTION
RETURN
END
Gatilhos
Observe que este é um gatilho de Update (FOR UPDATE) e
quando uma linha da tabela ItensVendidos for alterada, o gatilho
será acionado e fará um Update na tabela Produto, acrescentando
ao estoque a quantidade do produto devolvido e retirando do
estoque a quantidade do novo produto a ser levado pelo cliente
caso a data do pedido seja de até 5 dias. Caso contrário a alteração
é descartada.
Os comandos a seguir acionam a execução deste gatilho:
Update ItensVendidos set Cod_Prod = 1, Qtd_Prod = 8
where Num_Ped = 1 AND Cod_Prod = 3
Update ItensVendidos set Cod_Prod = 1, Qtd_Prod = 8
where Num_Ped = 2 AND Cod_Prod = 3
Gatilhos
Observe que um gatilho de update será acionado sempre que um
update for executado na tabela, não importando qual coluna dessa
tabela esteja sendo alterada.
Por exemplo, se você alterar a data de um pedido o gatilho
executara a movimentação de estoque prevista e a verificação de
data no comando desnecessariamente uma vez que os itens não
foram alterados
Para fazer com que o update da tabela seja acionado apenas se
uma determinada coluna da tabela tenha sido alterada, utilize a
função
If Update()
Gatilhos
declare @data datetime
set @data = (select min(Data_Ped) from deleted)
If Update()
IF UPDATE(Qtd_Prod) OR UPDATE(Cod_Prod)
BEGIN
O IF Update deve
IF (DateDiff(dd, @data, GetDate())) <= 5
ser inserido antes
BEGIN
do DateDiff
-- Baixa produtos inseridos
UPDATE Produto
SET Produto.Qtd_Est = Produto.Qtd_Est - Inserted.Qtd_Prod
FROM Inserted
Where Produto.Cod_Prod = inserted.Cod_Prod
-- Acrescenta produtos retirados
UPDATE Produto
SET Produto.Qtd_Est = Produto.Qtd_Est + deleted.Qtd_Prod
FROM deleted
WHERE Produto.Cod_Prod = deleted.Cod_Prod
Gatilhos
If Update
Como mostrado no exemplo anterior, o If Update deve ser
inserido antes dos comandos que alteram dados ou verificam
restrições de permissão de alteração dos dados.
Com isso os dados só serão alterados ou condições analisadas
caso as colunas envolvidas nestas operações sejam alteradas, caso
contrário, o comando de Update ocorrerá normalmente.
Gatilhos
Seqüência de execução dos Gatilhos
Como já foi dito anteriormente, uma tabela pode ter apenas
gatilhos de insert, delete e update, só que cada tabela pode ter
vários gatilhos de insert sobre ela, vários gatilhos de update e de
delete.
Suponha que você tenha criado uma tabela chamada TabA e tenha
criado para ela quatro gatilhos de insert com os seguintes nomes:
T_IncUM, T_IncDois, T_IncTres e T_IncQuatro. Esses quatro
gatilhos serão acionados, na ordem em que foram criados, quando
ocorrer um insert na tabela TabA.
Gatilhos
Se você não quiser que estes gatilhos seja acionados na ordem em
que foram criados, pode determinar qual deles deve ser acionado
primeiro e qual deles deve ser acionado por último. Veja como:
Exec SP_SetTriggerOrder ‘T_IncQuatro’, ‘FIRST’, ‘INSERT’
Exec SP_SetTriggerOrder ‘T_IncTres’, ‘LAST’, ‘INSERT’
Observe que você pode especificar uma determinada ordem de
execução para os gatilhos de Insert, uma outra ordem de
execução para os gatilhos de Update e uma outra ordem para os
gatilhos de Delete.
Gatilhos
Laboratório1: Se você quiser observar esta ordem de execução
sendo respeitada, execute no seu computador o exemplo seguinte:
CREATE TABLE Pessoa
CREATE TRIGGER T_IncPes1
(
ON Pessoa
)
Num_Pes int,
AFTER INSERT
Nome_Pes char(20)
AS PRINT ‘Teste 1’
GO
Gatilhos
Laboratório1: continuação
CREATE TRIGGER T_IncPes2
CREATE TRIGGER T_IncPes3
ON Pessoa
ON Pessoa
AFTER INSERT
AFTER INSERT
AS PRINT ‘Teste 2’
AS PRINT ‘Teste 3’
GO
GO
CREATE TRIGGER T_IncPes4
ON Pessoa
AFTER INSERT
AS PRINT ‘Teste 4’
GO
Gatilhos
Laboratório1: continuação
Execute os seguintes comandos, após compilar os gatilhos:
Exec SP_SetTriggerOrder ‘T_IncPes4’, ‘FIRST’, ‘INSERT’
Exec SP_SetTriggerOrder ‘T_IncPes2’, ‘LAST’, ‘INSERT’
Observe a seqüência de execução dos gatilhos a cada vez que um
insert for executado na tabela Pessoa.
Gatilhos
Gatilhos Instead Of
Faz com que o SQL Server execute as ações especificadas por
um gatilho, em vez de executar a ação que o acionou. No
exemplo seguinte, quando um usuário tentar executar uma
exclusão de algum funcionário da tabela funcionario, o triger
T_DelFun será acionado. Ele enviará uma mensagem para a
aplicação que tentou excluir o funcionário da tabela e a
Deleção do registro não será efetuada.
Gatilhos
/* A Tabela Funcionário */
CREATE TABLE Funcionario
(
Cod_Func int
not null,
Nome_Funcchar(20)
not null,
Sal_Func
)
GO
decimal(10,2) not null
Gatilhos
/* Gatilho que evita exclusão de Funcionario/*
CREATE TRIGGER T_DelFunc
ON Funcionario
INSTEAD OF DELETE
AS
RAISERROR(‘Operação Inválida’,16,1)
GO
Gatilhos
• Desabilitando e Reabilitando um Gatilho
De acordo com o exemplo dado anteriormente, o gatilho
T_DelFunc não permite que seja feita uma exclusão de dados da
tabela Funcionario. Se você precisar executar uma exclusão de
dados na tabela Funcionario, pode desabilitar o gatilho, excluir os
dados adequados e reabilitar o gatilho:
Gatilhos
ALTER TABLE Funcionario
DISABLE TRIGGER T_DelFunc
DELETE Funcionario WHERE Cod_Func = 1
ALTER TABLE Funcionario
ENABLE TRIGGER T_DelFunc
Gatilhos
Gatilho Aninhado
O aninhamento dos gatilhos é configurado por default. Isto faz
com que um gatilho possa, indiretamente, acionar outro gatilho e
assim por diante. Por exemplo, sunponha que você tenha cinco
tabelas: TabA, TabB, TabC, TabD e TabE, e também que tenha
criado um gatilho de insert sobre a tabela TabA que insere um dado
na tabela TabB, na TabB sunponha que você tenha um gatilho de
insert que insere um dado na tabela TabC, na TabC sunponha que
você tenha um gatilho que insere um dado na TabD e na TabD
sunponha que você tenha um gatilho de insert que insere um dado
na tabela TabE.
Gatilhos
Sendo assim, quando um usuário fizer uma inclusão de dados na
tabela TabA, todos os quatro gatilhos serão acionados, um após o
outro, e cinco inserções serão feitas, uma em cada tabela.
/* Criando as cincos tabelas */
CREATE TABLE TabA (Coluna char(1))
CREATE TABLE TabB (Coluna char(1))
CREATE TABLE TabC (Coluna char(1))
CREATE TABLE TabD (Coluna char(1))
CREATE TABLE TabE (Coluna char(1))
Gatilhos
/* Criando os quatro gatilhos */
CREATE TRIGGER T_IncTabA
ON TabA FOR INSERT
AS INSERT TabB VALUES (‘B’)
/* Criando os quatro gatilhos */
CREATE TRIGGER T_IncTabB
ON TabB FOR INSERT
AS INSERT TabC VALUES (‘C’)
Gatilhos
/* Criando os quatro gatilhos */
CREATE TRIGGER T_IncTabC
ON TabC FOR INSERT
AS INSERT TabD VALUES (‘D’)
/* Criando os quatro gatilhos */
CREATE TRIGGER T_IncTabD
ON TabD FOR INSERT
AS INSERT TabE VALUES (‘E’)
Gatilhos
/* Inserindo um dado na tabela TabA – Este insert aciona a
execução dos quatro gatilhos */
INSERT TabA VALUES (‘A’)
/* Verificando as 5 inserções */
SELECT * FROM TabA
SELECT * FROM TabB
SELECT * FROM TabC
SELECT * FROM TabD
SELECT * FROM TabE
Gatilhos
O aninhamento dos gatilhos não pode ultrapassar 32 níveis. Se este
limite for ultrapassado, o SQL Server emite uma mensagens de erro
e faz um ROLLBACK de todas as operações, inclusive daquela que
acionou a execução dos gatilhos.
Você pode configurar a opção que habilita e desabilita o
aninhamento de gatilhos, através do Enterprise Manager. Clique
com o botão direito do mouse sobre o nome do servidor,
selecione a opção properties. Depois clique em “Advanced” e
configure “Allow Triggers to Fire others”.
Gatilhos
Esta configuração pode ser feita com a system stored procedure
SP_Configure. O valor 1 configura a opção e o desconfigura.
Exec SP_Configure ‘Nested Trigger’ , 0
Gatilhos
Gatilhos Recursivos
Por default, o SQL Server não executa um gatilho que aciona a sim
mesmo. Por exemplo, suponha que você tenha uma tabela chamada
TabA e tenha também sobre ela um triger que insere um dado na
própria tabela TabA. Neste caso, o SQL Server inserirá apenas duas
linhas de dados na tabela TabA quando o usuário inserir
diretamente um dado nela. Veja o exemplo a seguir:
Gatilhos
/* Criando a tabela TabA */
CREATE TABLE TabA(Coluna char(1))
/* Criando um gatilho sobre a tabela TabA que insere dado na
própria tabela TabA */
CREATE TRIGGER T_IncTabA
ON TabA FOR INSERT
AS
INSERT TabA VALUES (‘A’)
Gatilhos
/* Inserindo um dado na tabela TabA */
INSERT TabA VALUES (‘A’)
/* Verifique que para esta inserção o SQL Server insere na tabela
TabA dois registros */
SELECT * FROM TabA
Gatilhos
Se você quiser que a recursividade seja acionada, pode fazer esta
configuração de uma das duas formas:
Utilizando o Enterprise Manager, clique com o botão direito sobre
o nome do banco de dados, escolha a opção Properties. Na tela que
segue escolha a guia Options. Em seguida configure a opção
“Recursive Triggers”.
Se quiser, pode realizar esta configuração executando o comando
ALTER DATABASE. Observe:
ALTER DATABASE Editora SET RECURSIVE_TRIGGERS ON
Gatilhos
Quando você configura seu database para acionar gatilhos
recursivos, terá que proporcionar uma forma, dentro do gatilho,
para que o SQL Server não exceda os 32 níveis permitidos no
aninhamento dos gatilhos. Por exemplo:
CREATE TRIGGER T_IncTabA
ON TabA FOR INSERT
AS
IF (SELECT Count(*) FROM TabA, Inserted
WHERE TabA.Coluna = Inserted.Coluna) <= 10
INSERT TabA VALUES (‘A’)
Gatilhos
Trigers Sobre Views
Se precisar, você pode criar gatilhos INSTEAD OF sobre views.
Observe:
CREATE VIEW dbo.V_Func
AS
SELECT Nome_Func, Sal_Func
FROM dbo.Funcionario
Gatilhos
/* Criando o gatilho sobre a visão */
CREATE TRIGGER T_DelVFunc
ON dbo.V_Func
INSTEAD OF DELETE
AS RAISERROR(‘Exclusão Rejeitada’,16,1)
Nota: você não pode colocar a cláusula WITH CHECK OPTION
em uma view se ela tiver que ter um gatilho sobre ela.
Gatilhos
Comandos que não podem ser colocados em um gatilho (SQL
Server):
•ALTER DATABASE
•RECONFIGURE
•CREATE DATABASE
•RESTORE DATABASE
•DISK INIT
•RESTOTE LOG
•DISK RESIZE
•DROP
•LOAD DATABASE
•LOAD LOG
Gatilhos
For ou After Gatilhos
Os after gatilhos são executados depois que a ação de Insert, Delete
ou Update que os acionou for executada. Especificar a cláusula
AFTER é a mesma coisa que especificar a cláusula FOR. After
gatilhos só podem ser especificados em tabelas, não em views.
Gatilhos
Criando Tabela de Log
CREATE TABLE [dbo].[Log](
[IDLog] [int] IDENTITY(1,1) NOT NULL,
[Data] [datetime] NULL,
[Tabela] [varchar](50) NULL,
[Campo] [varchar](50) NULL,
[Conteudo] [varchar](50) NULL,
[Operacao] [char](1) NULL
) ON [PRIMARY]
Gatilhos
Criando Log de alterações com a utilização de gatilhos
CREATE TRIGGER T_FuncionarioLog
ON Funcionario
FOR UPDATE
AS
declare @valor varchar (50)
if Update(Cod_Func)
begin
select @valor = cast(cod_func as varchar(50)) from
inserted
insert into Log
values (getdate(),'Funcionario','Cod_Func',@valor,'U')
end
Gatilhos
Criando Log de alterações com a utilização de gatilhos
if Update(Nome_Func)
begin
select @valor = Nome_Func from inserted
insert into Log
values (getdate(),'Funcionario','Nome_Func',@valor,'U')
end
if Update(Sal_Func)
begin
select @valor = cast(sal_func as varchar(50)) from
inserted
insert into Log
values (getdate(),'Funcionario','Sal_Func',@valor,'U')
end
Gatilhos
Criando Log de alterações com a utilização de gatilhos
ALTER TRIGGER T_DelFunc
ON Funcionario
INSTEAD OF DELETE
AS
declare @valor varchar (50)
select @valor = cast(cod_func as varchar(50)) from deleted
insert into Log
values (getdate(),'Funcionario','*','Tentativa de exclusão
do funcionario ' + @valor,'D')
GO
Download