A frase..

"A bondade tornou feliz minha vida."



quarta-feira, 28 de dezembro de 2011

Trigger para Delete

Caso você não tenha lido "Trigger -- Quero aprender a fazer uma!!!" é só clicar para dar uma conferida. Mas como eu sei que provavelmente você não irá fazer isso então eu ofereço para você o ambiente:


CREATE DATABASE bd_Trigger
USE bd_Trigger



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))

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)


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)


Após todo os preparativos vamos esclarecer qual o sentido dessa trigger. A ideia é simples: A trigger vai ser disparada nas opções de DELETE. Quando isso acontecer um registro será gravado para cada produto excluído na tabela ProdutosExcluidos, criando então um log.


Faça os testes a seguir pra termos certeza que as tabelas necessárias para a atuação das triggers resultarem em sucesso:




SELECT * FROM ProdutosExcluidos
SELECT * FROM Produto

Os resultados devem ser esses:


Agora iremos construir a trigger. Você poderá colar tudo em um query, mas aconselho que leia e entenda, isso é muito importante e será muito útil futuramente.

--Criando uma Trigger para deletes

CREATE TRIGGER tr_ExclusaoProduto ON Produto FOR DELETE

--Agora as variáveis responsáveis por toda a mágia

AS
DECLARE @cd_Produto INT
DECLARE @ds_Produto VARCHAR(50)
DECLARE @dt_Exclusao SMALLDATETIME

--Atribuindo valores da tabela deleted para as variáveis  equivalentes

SELECT @cd_Produto=cd_Produto,@ds_Produto = ds_Produto , @dt_Exclusao  = GETDATE() FROM deleted

--Com esses valores já disponíveis, faço uma inserção na tabela ProdutosExcluidos

INSERT INTO ProdutosExcluidos 
VALUES ( @cd_Produto , @ds_Produto , @dt_Exclusao) 


Agora pode executar.

Para testarmos a trigger iremos fazer um teste. Suponhamos que o produto Martelo foi tirado das prateleiras, ou seja, não será mais vendido. Obviamente  teremos de excluir tal item do banco. Nesse caso ao fazermos:

DELETE FROM Produto WHERE cd_Produto=2


(código 2, pois esse é o código do martelo, visivelmente apresentado no resultado do teste antes de construir a trigger)


O resultado deve ser esse:




Por que duas linhas separadamente afetadas? A resposta é simples! Faça os seguintes teste e veja os resultados:



SELECT * FROM Produto
SELECT * FROM ProdutosExcluidos




Veja que o item "Martelo" Desapareceu da tabela "Produto" e em compensação surgiu um registro "Martelo" em "ProdutosExcluidos". Tudo isso foi possível por conta da Trigger criada anteriormente.




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/