A frase..

"A bondade tornou feliz minha vida."



sexta-feira, 16 de dezembro de 2011

Trigger -- Quero aprender a fazer uma!!!

Boa Noite queridos compatriotas!!! Mais uma vez aqui unidos, juntos, misturados para mais uma aventura pelo mundo do SQL. Para vocês trago a famosa Trigger. Com alguns exemplos postos em questão fica muito mais fácil de compreender. As Triggers são conhecidas como Gatilhos. Esse nome é definido assim pois é disparado um comando a partir do momento em que foi usado um insert, um delete ou mesmo um update. Como isso?? Um exemplo simples, apenas a título de conhecimento, imaginemos que por um acaso, em uma bela manhã ensolarada, acordei e resolvi fazer um insert. Ótimo, mas ao mesmo tempo que tomar alguns dados e inserílos em outra entidade (tabela), porém não quero todo o insert e sim alguns dados apenas e com algumas condições.   Ah eu até poderia fazer vários Inserts em todas as entidades... Mas o problema é "e se de repente eu resolva fazer um novo insert e outro e mais outro, inúmeros inserts e que todos tenham exijam um parâmetro, um parâmetro responsável por enviar ou não tais dados a outras tantas entidades?", para isso precisamos de algo que faça esse serviço. Vamos começar então.


Criando o ambiente


Ótimo já chegamos até aqui. Se você teve paciência e foi vitorioso(a), vamos preparar o ambiente. Todos estão com o SQL Server aí?? Quem não pode buscá-lo vamos precisar. 
Conecte-se e crie um banco:


CREATE DATABASE bd_Trigger
USE bd_Trigger


O próximo passo é criar as entidades. Por elas criaremos toda a ordem de negócio, simples por sinal. A propósito vou dedicar um post com uma conversa sobre modelagem, muito útil.
Bom voltando iremos criar quatro entidades: Produto, LogPreco, PedidoCompra, ProdutosExcluidos.


Veja:



CREATE TABLE ProdutosExcluidos(
cd_Produto INT NULL,
ds_produto VARCHAR(50) NULL,
dt_exclusao SMALLDATETIME NULL)
CREATE TABLE Produto(
cd_Produto INT NOT NULL,
ds_Produto VARCHAR(50) NOT NULL,
vl_Produto MONEY NULL,
qt_Estoque INT NULL,
qt_Estoquemin INT NULL,
 CONSTRAINT PK_Produto PRIMARY KEY CLUSTERED(cd_Produto))

 CREATE TABLE PedidoCompra(
cd_PedidoCompra INT IDENTITY NOT NULL,
cd_Produto INT NOT NULL,
dt_Solicitacao SMALLDATETIME  NOT NULL, 
qt_Compra INT NOT NULL,
 CONSTRAINT PK_PedidoCompra PRIMARY KEY CLUSTERED (cd_PedidoCompra))

 CREATE TABLE LogPreco(
cd_LogPreco INT IDENTITY NOT NULL,
dt_Alteracao SMALLDATETIME NOT NULL,
cd_Produto INT NOT NULL,
vl_ProdutoAntigo MONEY NOT NULL,
vl_ProdutoNovo MONEY NOT NULL,
 CONSTRAINT PK_LogPreco PRIMARY KEY CLUSTERED (cd_LogPreco))

Ótimo. Agora vamos fazer alguns relacionamentos. Não havia feito antes porque acho muito mais interessante fazê-lo já tendo as tabelas construídas. É uma forma de visualizar o que possuo antes de sair disparando relacionamentos. Veja:


ALTER TABLE LogPreco
ADD CONSTRAINT fk_LogPreco
FOREIGN KEY (cd_Produto)
REFERENCES Produto (cd_Produto)


ALTER TABLE PedidoCompra
ADD CONSTRAINT fk_PedidoCompra
FOREIGN KEY (cd_Produto)
REFERENCES Produto (cd_Produto)


Após tudo estar preparado vamos para a parte que todos esperavam.. a Trigger.


Vamos começar com uma trigger que deverá ser disparada nas operações de UPDATE, o que significa que quando fizermos um update ela automaticamente irá disparar valores. Como assim? Vamos supor que eu queira alterar o valor de alguns preço na tabela de Produto e ainda assim queira manter um registro dos valores antigos. Ao invés de fazer a alteração e depois inserir manualmente o que seria uma perca de tempo e poderia até gerar a inconsistência do dado ou mesmo a não inserção do valor alterado na tabela  de histórico, a trigger automaticamente faria esse trabalho sujo (sahuasu). Então a ideia é fazer um trigger ligando a tabela Produto e LogPreco. Vamos deixar claro que cd_logpreco é autonumeração, ou seja não requer digitação, aliás nem é possível tal coisa, isso significa que não deve constar na lista de campos quando a trigger for executar o insert na tabela Log.


Primeiro vamos inserir alguns valores:



INSERT Produto([cd_Produto], [ds_Produto] ,  [vl_Produto] , [qt_Estoque] , [qt_Estoquemin]) VALUES  ( 1 , 'Prego' , 1.0000 , 25 , 10) 
INSERT Produto ( [cd_Produto] , [ds_Produto] , [vl_Produto] , [qt_Estoque] , [qt_Estoquemin]  VALUES  ( 2 , 'Martelo' , 50.0000, 30 , 15) 
INSERT Produto ( [cd_Produto] , [ds_Produto] , [vl_Produto] , [qt_Estoque] , [qt_Estoquemin]  VALUES  ( 3 , 'Parafuso' , 2.0000 ,  10 ,  5) 
INSERT Produto ( [cd_Produto] , [ds_Produto] , [vl_Produto] , [qt_Estoque] , [qt_Estoquemin]   VALUES  ( 4 , 'Chave de Fenda' , 25.0000 , 15 , 10)


Agora faça os testes a seguir:



SELECT * FROM LogPreco
SELECT * FROM Produto


Este é o resultado que deve ser visualizado:


Você vai poder colar tudo se precisar na query, já que os comentários estão..comentados!


--Agora vamos criar a Trigger:


CREATE TRIGGER tr_MudancaValor ON Produto FOR UPDATE


--Vamos comentar essa primeira linha. Aqui a ideia é fazer um gatilho que
--dispare em um Update, certo? Então repare que utilizamos "FOR UPDATE" ou seja para um update, em uma tradução grosseira.



--Nesse momento vamos criar variáveis. A função de cada variável vai variar, literalmente da 
--variação da nossa necessidade com relação toda a regra de negócio. Repare a 
--indispensabilidade em informar o tipo da variável. São iguais as dos campos que vão atribuir 
--os valores. Verá logo o porque.


AS
DECLARE @dt_alteracao SMALLDATETIME
DECLARE @cd_produto INT
DECLARE @vl_produtoant MONEY
DECLARE @vl_produtonovo MONEY

--Algo que talvez você não conheça, mas será apresentado nesse momento. Pois bem, cada vez
--que você faz um Update, duas tabelas temporárias, de uso do banco, mas que nós muitos
que --elas fazem, ou melhor dizendo armazenam (temporariamente, importante frizar) é os novo
--espertos iremos usufruir desse bem, ssão criadas. Elas se chamam Inserted e Deleted. O
s --valores e os antigos, nesta oredem. Nesse caso o que você tinha gravado, temporáriamente está
nca, jamais, never, ouviu, never, diga que além dessas duas tabelas existe --uma cham
--na tabele Deleted e o que você resolveu alterar está em Inserted. Pesso agora muita --atenção, n
uada Update, está me lendo?? Continuando...



--Repare que as variáveis @cd_produto e @vl_produtoant recebem os valores de cd_Produto 
--e vl_Produto, nesta ordem, de Deleted. O que significa que nesse momento estou usando 
--do poderoso recurso de coletar dos antigos registros (momentaneos) da tabela Deleted.

SELECT @cd_produto=cd_Produto,@vl_produtoant=vl_Produto FROM DELETED
--Faço o mesmo com a tabela Inserted para a variável @vl_produtoant. A diferença é 
--para @dt_alteracao que utilizo a função GETDATE(), para recuperar a data da "alteração".
SELECT @dt_alteracao = GETDATE() , @vl_produtoant  = vl_Produto FROM INSERTED

--No final inserimos todos os valores dessas vaiáveis nos campos da tabela LogPreco e temos

--uma inserção automática e com valores que custariam muito tempo para buscálos, analisá-los
--e inserí-los manualmente.

INSERT INTO LogPreco VALUES 
(@dt_alteracao,@cd_produto , @vl_produtoant , @vl_produtoant)


Vamos fazer um teste antes de executar a Trigger:


SELECT * FROM LogPreco

Esse deve ser o resultado:


Facinante, não? Também achei. Bom agora o momento que todos esperavam... Vamos testar a Trigger!

UPDATE Produto SET vl_Produto*=1.50

"Mas... você disse que íamos testar a Trigger e não fazer um Update!"

Sim, e nós testamos! Claro que muitos "mas..." devem estar surgindo em sua cabeça, mas é assim mesmo. Lembra que a ideia de um gatilho é justamente essa (deste ao menos) "Ao fazer um Update, que certos dados sejam armazenados automaticamente na tabela LogPreco", então foi o que fizemos, o resultado é esse:


Repare que 1 e 4 linhas foram afetadas, 4 alterações e 1 que você entenderá porque após esse teste:

SELECT * FROM LogPreco

Resultado:


Faça outros testes de Update... e veja os resultados! Agora deixo vocês e aguardem o próximo post: "Trigger para Delete". Até mais ^^ o/



















2 comentários:

  1. Gostei do post.
    Sobre a trigger, ela fica "ativa" full time?.
    Ou seja, se eu fechar o banco e depois abri-lo de novo ela ira funcionar sem que eu precise chama-la?

    ResponderExcluir
    Respostas
    1. Realmente, faz muito tempo que você fez a pergunta. Nesse caso peço desculpas. A trigger continuará no banco, mas precisará sempre ser chamada. Mas em uma aplicação ela pode ser muito útil. Ao invés de encher de linhas de código com comandos string, uma apenas com trigger fará todos os comandos que estiverem inclusos.

      Excluir