Desenvolvendo Triggers em SQL Server, Oracle, Firebird e Postgres
Confira nesse artigo um comparativo entre os três principais bancos de dados: Oracle, SQL Server, Firebird e PostgreSQL. Veremos como usar triggers, comentários e a condicional IF.
Ao trabalharmos com bancos de dados, temos a ideia de que eles trabalham praticamente da mesma forma, o que não é verdade, pois cada um possui as suas peculiaridades. Neste artigo apresentaremos um comparativo entre os principais bancos de dados em relação a alguns itens que são importantes no momento em que trabalhamos com aplicações diversas, como é o caso de utilização de triggers, comentários e condicionais comparação ocorrerá entre os bancos de dados SQL Server, Oracle e Firebird.
Utilização de triggers
No que diz respeito a triggers, o Oracle e o SQL Server possuem conceitos diferentes para a sua criação.
O SQL Server possui uma abordagem baseada em conjuntos, tendo que as linhas que são afetadas por uma modificação de dados, sejam insert, update ou delete, são armazenadas nas tabelas temporárias inserted e deleted. Em caso de triggers regulares do tipo DML (Data Model Language), DDL (Data Definition Language) e de Logon, estas serão sempre executadas após a passagem de uma instrução. Neste momento, tem-se que uma “imagem” do registro é criada antes da informação definitiva ser armazenada na tabela e, em seguida, a “imagem” é eliminada após o registro ser inserido definitivamente na tabela. Ambos podem ser acessados de dentro da trigger. No entanto, quando é necessário, pode-se juntar as tabelas temporárias inserted e deleted para realizar a atualização da tabela quando a trigger for executada.
No Oracle são encontrados os termos AFTER e BEFORE no corpo das triggers, que podem ser executadas tanto por linha quanto por declaração. Com isso, as triggers utilizando BEFORE são disparadas antes da ocorrência de uma ação. Já as triggers apresentadas com o AFTER ocorrem após a ação ter sido executada.
Nos três bancos temos momentos como no Oracle. Porém, no SQL Server temos os momentos AFTER e INSTEAD OF, mas não o momento BEFORE, enquanto que o Firebird suporta múltiplos disparos da trigger a nível de linha por tabela, o que não ocorre com o Oracle.
No que diz respeito ao Oracle, quando trabalhamos com triggers BEFORE, estas não podem ver uma imagem que seja declarada como AFTER, onde de igual forma uma trigger declarada como AFTER não poderá ver uma imagem que seja declarada com BEFORE. Apenas triggers de linha podem visualizar as imagens BEFORE e AFTER, ao mesmo tempo. Nestes casos em que utilizamos as triggers de linha, podemos fazer referência aos valores contidos nas colunas, sabendo qual a informação que existia antes de uma atualização e após essa atualização ter sido realizada. Para isso, a Oracle oferece os pseudorecords OLD e NEW, os quais são utilizados para acessar as informações antigas e novas, respectivamente. Com base nisso, podemos dizer que o OLD e o NEW são, portanto, semelhantes as tabelas temporárias deleted e inserted, presentes no SQL Server, com a diferença que estes estão em nível de linha. Outra propriedade presente nas triggers da Oracle, que também é uma fonte regular de erros, é quando a tabela na qual a trigger está atuando é chamada por uma outra operação qualquer como, por exemplo, uma consulta.
Uma outra diferença entre o Oracle e o Firebird é que o Firebird não levanta exceções referentes a mutação de tabelas, como a Oracle faz, e as triggers, por padrão, podem ser aninhadas e recursivas, diferentemente do SQL Server, que não aceita recursividade por padrão. No SQL Server as operações de trigger recursiva são possíveis, mas pelo menos para triggers normais este não é o comportamento padrão, além de que triggers disparando de forma recursiva precisam ser definidas explicitamente como uma opção de servidor.
As triggers do Firebird usam as variáveis de contexto NEW e OLD e provêm flag de inserção, atualização e exclusão para indicar o status atual da trigger.
Já o PostgreSQL é muito parecido com o Firebird, pois para obtermos as informações basta atribuirmos o prefixo OLD para realizarmos as consultas, e o NEW quando for um novo valor. Eles, por conseguinte, são diferentes do Oracle devido a utilização do (:) antes dos termos OLD e NEW. Com relação a compilação das triggers, temos que o banco de dados exclui normalmente as versões anteriores, caso já tenham sido compiladas anteriormente. Dessa forma, tanto para o Postgres quanto para o Oracle, basta utilizarmos a instrução CREATE ou REPLACE, enquanto que no SQL Server precisamos verificar no dicionário de dados se o objeto com mesmo nome já existe.
Antes de começarmos com os exemplos, torna-se necessário a visualização da sintaxe apresentada por cada uma das bases de dados para a criação de uma trigger, o que podemos ver de acordo com as Listagens 1, 2, 3 e 4, respectivamente Oracle, SQL, Firebird e PostgreSQL. Vale salientar que as triggers que serão vistas serão referentes a operações DML.
CREATE [OR REPLACE ] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF }
{INSERT [OR] | UPDATE [OR] | DELETE}
[OF col_name]
ON table_name
[REFERENCING OLD AS o NEW AS n]
[FOR EACH ROW]
WHEN (condition)
DECLARE
Declaration-statements
BEGIN
Executable-statements
EXCEPTION
Exception-handling-statements
END;
-- Sintaxe do SQL Server
Trigger on an INSERT, UPDATE, or DELETE statement to a table or view (DML Trigger)
CREATE TRIGGER [ schema_name . ]trigger_name
ON { table | view }
[ WITH <dml_trigger_option> [ ,...n ] ]
{ FOR | AFTER | INSTEAD OF }
{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
[ WITH APPEND ]
[ NOT FOR REPLICATION ]
AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME <method specifier [ ; ] > }
<dml_trigger_option> ::=
[ ENCRYPTION ]
[ EXECUTE AS Clause ]
<method_specifier> ::=
assembly_name.class_name.method_name
{CREATE | RECREATE | CREATE OR ALTER} TRIGGER name FOR {TABLE name | VIEW name}
[ACTIVE | INACTIVE]
{BEFORE | AFTER}
{INSERT [OR UPDATE] [OR DELETE] | UPDATE [OR INSERT] [OR DELETE] | DELETE [OR UPDATE] [OR INSERT] }
[POSITION n] AS
BEGIN
.....
END
CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
ON table_name
[ FROM referenced_table_name ]
[ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } ]
[ FOR [ EACH ] { ROW | STATEMENT } ]
[ WHEN ( condition ) ]
EXECUTE PROCEDURE function_name ( arguments )
Vejamos um exemplo simples de como podemos criar triggers para cada uma das bases de dados apresentadas neste artigo. Começaremos vendo a criação de uma trigger no Oracle, onde apresentaremos uma simples mensagem contendo os valores de salários novos, antigos e a diferença entre eles, como apresentado pela Listagem 5.
CREATE OR REPLACE TRIGGER Muda_SalarioFuncionario
BEFORE DELETE OR INSERT OR UPDATE ON Funcionarios
FOR EACH ROW
WHEN (new.IdFuncionario > 0)
DECLARE
diferenca number;
BEGIN
diferenca := :new.salarioFuncionario - :old.salarioFuncionario;
dbms_output.put('o salário antigo do funcionário foi de ' || :old.salarioFuncionario);
dbms_output.put(' o salário atual do funcionário é de ' || :new.salarioFuncionario);
dbms_output.put_line('A diferença entre os salários do funcionário é de ' || diferenca);
END;
/
A trigger é disparada quando uma das operações DML (INSERT, UPDATE e DELETE) é realizada sobre a tabela. Devido ao fato de estarmos utilizando a palavra-chave BEFORE, a trigger pode acessar os novos valores antes que estes sejam inseridos na tabela, além da possibilidade de poder alterar o valor utilizando a atribuição :NEW.Column_name.
Já com relação ao AFTER, este também pode ser utilizado, mas apenas para consultar ou alterar as informações presentes na tabela, após as mudanças iniciais terem sido realizadas e a tabela estiver com os dados já gravados e consistentes. Estamos utilizando neste caso a cláusula FOR EACH ROW, para que possamos executar a instrução várias vezes, o que ocorre em casos de atualização ou exclusão de várias linhas ao mesmo tempo.
Após criarmos a trigger, realizaremos agora uma inserção na tabela de Funcionários e em seguida, uma atualização no item salarioFuncionario para que possamos ver as informações serem apresentadas. Para que possamos inserir os dados de teste na tabela, temos que a query de inserção e de update que serão utilizadas estão sendo apresentadas pela Listagem 6.
-- Instrução de Insert para gravar os registros dos funcionários.
INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario)
VALUES (1, ‘Edson Dionisio’, ‘Rua dos testes’, 3000);
INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario)
VALUES (5, ‘José Edson dos Santos’, ‘Starling city’, 2000);
INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario)
VALUES (9, ‘Tatsu Yamashiro’, ‘Rua dos Navegantes’, 4500);
INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario)
VALUES (15, ‘Carlos Edson Andrade’, ‘Rua dos testes’, 1700);
INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario)
VALUES (25, ‘Mariana Ximenes’, ‘Rio de Janeiro’, 12000);
-- Instrução de update para atualizar os registros dos funcionários.
UPDATE Funcionarios SET salarioFuncionario = salarioFuncionario + 1500
WHERE NomeFunc LIKE ‘%Edson%’;
Ao utilizarmos a instrução UPDATE, ela irá disparar a trigger para cada linha que deverá ser atualizada, apresentando dessa forma tanto o salário novo do funcionário, quanto o antigo e ainda a diferença entre os valores.
O próximo exemplo que veremos será referente a trigger do banco de dados Firebird, onde utilizaremos o mesmo conceito de mudança de salário, como mostra a Listagem 7.
create trigger audita_salarioFuncionario for funcionario
after update
as
begin
if (old.salarioFuncionario <> new.salarioFuncionario) then
insert into historico
(idfuncionario, datamodificacao,
usuario, salarioFuncionario_antigo,
percentagem
)
values (
old.idfuncionario, 'now', user, old.salarioFuncionario,
(new.salarioFuncionario/old.salarioFuncionario-1) * 100
);
end;
A trigger realiza uma atualização das informações principais referentes a mudança do salário de um funcionário, apresentando além disso a porcentagem de mudança entre o salário antigo e o novo salário. Mas para isso, verificamos antes se o novo salário é diferente do antigo para que possamos realizar as operações necessárias.
Por último, temos o exemplo que mostra como criar a trigger no SQL Server, que tem o objetivo de que, quando um registro for inserido na tabela funcionários, também realizemos a inserção dos dados na tabela de backup, mantendo uma cópia de algumas das informações do funcionário na tabela funcionarioBackup. Após a criação das tabelas, iremos preenchê-las com alguns dados, como mostra a Listagem 8.
create trigger trg_copiafuncionario
on dbo.funcionario
after insert as
begin
insert into funcionarioBackup
select * from inserted
end
go
Ao criarmos nossa trigger, inserimos um novo registro na tabela de funcionários, onde neste mesmo instante inserimos um registro na tabela de funcionarioBackup, pois no momento da criação da trigger utilizamos o AFTER INSERT na tabela de funcionarios, o que nos diz que, após a inserção do registro na tabela de funcionários, será realizada a inserção dos mesmos dados na tabela de cópia.
A tabela INSERTED é uma tabela temporária, que tem por objetivo manter apenas as linhas que são inseridas e, devido a isso, escolhemos esta opção para manter as informações sincronizadas em ambas as tabelas. Para testarmos a nossa trigger, iremos inserir alguns registros na tabela de funcionários e, em seguida, realizaremos um select na tabela de funcionarioBackup para ver o resultado, como mostra a Listagem 9.
insert into funcionario (codigoFunc, nomefunc, email, telefone)
values (5, 'Edson Dionisio', 'edson.dionisio@gmail.com', '81997402801')
insert into funcionario (codigoFunc, nomefunc, email, telefone)
values (8, 'Maitê Silva', 'maite.silva@gmail.com', '81997402801')
insert into funcionario (codigoFunc, nomefunc, email, telefone)
values (12, 'Marilia Késsia', testekessia@gmail.com', '81997402801')
select * from funcionario
select * from funcionariobackup
go
Após a inserção ter sido realizada na tabela de funcionários, podemos ver que as mesmas informações foram salvas na tabela de funcionarioBackup, como era de se esperar.
Podemos ver com isso que a utilização das triggers facilita bastante os processos repetitivos, auditoria de informações, relatório de erros, dentre outras opções que podemos achar necessário para a criação de uma trigger.
Um novo exemplo simples a ser adicionado será com relação a base de dados Postgres: nele iremos inserir dados numa tabela chamada Item e verificaremos a quantidade de itens existentes. Caso esta quantidade atinja o seu limite, teremos uma mensagem de erro disparada pela Trigger. Para vermos este exemplo podemos primeiramente criar uma tabela no Postgres, chamada Item, e a ela adicionaremos apenas os campos idItem e produto. Em seguida, teremos a nossa trigger definida de acordo com a Listagem 10.
-- Criando a função
CREATE OR REPLACE FUNCTION item()
RETURNS trigger AS
$BODY$
Declare LTOTALITENS INT;
Declare LNOTA INT;
BEGIN
-- Quantidade de itens inseridos na nota
Select COUNT(*) into LTOTALiTENS From "Item";
If (LTOTALITENS > 6) Then
Raise Exception 'A nota % já está cheia. Inclusão cancelada.', LNOTA;
End If;
Return New;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION item()
OWNER TO postgres;
-- Criando a trigger
create trigger "InsereItem"
BEFORE INSERT ON "Item" FOR EACH ROW
EXECUTE PROCEDURE ITEM();
Trabalhando com Comentários
No que diz respeito a comentários, não há muita diferença entre as bases de dados SQL Server, Oracle e Firebird, pois ambas permitem tanto comentário de linha quanto comentários de múltiplas linhas.
Os comentários de uma linha são representados pelos símbolos (--) ou (/**/) para comentários de múltiplas linhas. Um exemplo que representa ambos os bancos de dados pode ser visto na Listagem 9.
/* Criação de uma tabela Clientes
* que tem apenas o objetivo de apresentar os comentários de múltiplas linhas.
*/
create table Clientes (
nomeFunc varchar(100), -- NomeFunc do cliente
enderecoFunc varchar(255), /* este pode conter o endereço do cliente.
Para este são adicionadas as seguintes informações:
* Rua,
* cep,
* Estado, etc
*/
telefone varchar(14), -- Campo para atribuição do telefone
email varchar (200) -- Campo responsável pela email do usuário
)
O que ocorre no Oracle é que podemos adicionar instruções SQL como parte do código da aplicação que executa as instruções, além de adicionar comentários associados a esquema ou objetos nonschema individuais, que são armazenados no dicionário de dados junto aos metadados dos próprios objetos.
Para comentários referentes a Schema e objetos NonSchema, podemos usar o comando COMMENT para associar um comentário com um objeto de esquema (tabela, view, operadores, indextype, etc). Além disso, temos a possibilidade de criar um comentário sobre uma coluna específica, que é parte de um objeto de esquema da tabela. Os comentários associados a esquemas e objetos nonschema são armazenados no dicionário de dados.
Trabalhando com IF
De igual forma às linguagens de programação, as estruturas condicionais IF...ELSE são utilizadas para que sejam estabelecidas condições no decorrer da execução do fluxo de código, onde temos a atribuição de duas ou mais possibilidades de resultado que podem ser executados.
No Oracle, a instrução IF-THEN-ELSE pode variar, onde dependendo do caso, podemos usar a sintaxe IF-THEN-ELSIF, quando precisamos executar um conjunto de instruções quando condition1 é TRUE ou um conjunto diferente de declarações passa a ser TRUE. Um exemplo dessa utilização pode ser visto na Listagem 10.
IF salarioFuncionario >= 800 AND salarioFuncionario <= 1400 THEN
profissao := 'Estagiário em Desenvolvimento de sistemas';
ELSIF salarioFuncionario > 1400 and salarioFuncionario <= 4000 THEN
profissao := 'Desenvolvedor Junior';
ELSIF salarioFuncionario > 4000 and salarioFuncionario <= 9000 THEN
profissao := ' Desenvolvedor Junior';
ELSE
profissao := 'Não há uma profissão cadastrada';
END IF;
Com o Firebird não ocorre essa variação, pois ele apresenta apenas a estruturação IF-THEN-ELSE. No entanto, ele possui uma particularidade que é uma forma diferente de escrever a instrução, a qual é apresentada por IIF(), que pode ser comparada a uma operação ternário do C#. A sintaxe para esse caso é apresentada na Listagem 11.
IIF (<condição>, ResultadoA, ResultadoB)
<condição> ::= Expressão booleana.
Para que possamos entender melhor esta sintaxe, apresentaremos tanto a forma normal da estrutura de fluxo apresentada pelo Firebird quanto a sintaxe apresentada pela Listagem 11. Vejamos então as Listagens 12 e 13.
if (salarioFuncionario < 3000) then
funcionario = 'Pode ser estagiário!';
else
funcionario = 'É desenvolvedor pleno';
select iif( sexo = 'Masculino', 'Sr', 'Sra' ) from Clientes
Por último, temos o SQL Server, que possui a estrutura representada apenas por IF-ELSE, e apresentando de igual forma a estrutura particular IIF(). Podemos ver um exemplo na Listagem 16.
-- Utilizando a base de dados exemplo do SQL Server.
USE AdventureWorks2012;
GO
/* Realizando a contagem de produtos que tenham o nomeFunc iniciado por touring-3000
E em caso de existir, apresentar uma mensagem.
*/
if (select count(*) from production.product where name like 'touring-3000%' ) > 5
print 'Existem mais que 5 bicicletas touring-3000 bicycles cadastradas.'
else
print 'Existem 5 ou menos bicicletas touring-3000 na base de dados.' ;
go
Veja que a diferença na sintaxe não é tão grande em relação a forma de processamento, principalmente no que tange o uso de triggers.
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo