Sie sind auf Seite 1von 6

Desenvolvendo Triggers em SQL Server, Oracle,

Firebird e Postgres
http://www.devmedia.com.br/articles/viewcomp.asp?comp=5625&hl=*oracle*

empresas que desenvolvem aplicativos para diversos bancos de dados passam por
uma dúvida sobre como tratar as regras de negócio: elas ficam na aplicação ou
devem ser escritas no banco de dados.
quando é optado por deixar as regras na aplicação, a vantagem está na
portabilidade, porque não será necessário reescrever estas regras em cada
linguagem que é disponibilizada pelos fornecedores de sgbd.
por outro lado quem opta por colocar tudo no banco de dados garante uma maior
integridade das informações, e normalmente tem algum ganho de desempenho por
deixar procedimentos pesados no lado do servidor do banco de dados.
neste artigo será abordada somente uma pequena parte deste assunto. o objetivo é
comentar algumas das diferenças existentes na criação de triggers nos seguintes
bancos de dados: sql server, oracle, firebird e postgres.

1. modelo empregado pelo artigo


para explicarmos as diferenças entre os bancos de dados escolhidos para este
artigo, criaremos a mesma trigger em todos eles. o objetivo desta trigger será
impedir que em uma nota fiscal sejam incluídos mais do que trinta itens, dando
uma mensagem sempre que isto acontecer.
foram definidas duas tabelas simplificadas: uma de nota fiscal (tabela 1) e outra
com os itens da nota fiscal (tabela 2)

campo tipo
#idnotafiscal inteiro
idcliente inteiro
datavenda data
tabela 1. definição da tabela de notas fiscais

campo tipo
#idnotafiscal inteiro
#iditem inteiro
idproduto inteiro
preço unitário numérico
quantidade numérico
tabela 2. definição da tabela de itens da nota fiscal

2. listagens das triggers

a seguir estão relacionados os códigos da trigger nos quatro bancos de dados

listagem 2. trigger para o sql server

-- exclui a trigger caso ela já exista.

if object_id ('tr_in_item','tr') is not null


drop trigger tr_in_item
go

create trigger tr_in_item


on item
after insert
as
declare
@ltotalitens int,
@lnota int
begin
-- seleciona o código da nota que está sendo incluída.
select @lnota = idnota
from inserted;
-- totaliza a quantidade de itens já cadastrados na nota.
select @ltotalitens = count(*)
from item
where idnota = @lnota;
-- verifica se existem mais do que 30 itens na nota.
if @ltotalitens > 30
begin
raiserror('a nota %d já está cheia. inclusão cancelada.',16,1,@lnota);
rollback;
end
end
go

listagem 3. trigger para o firebird


create exception nota_cheia 'a nota já está cheia. inclusão cancelada.';

create trigger tr_in_item


for item
after insert
as
declare ltotalitens integer;
declare lnota integer;
begin
-- seleciona o código da nota que está sendo incluída.
lnota = new.idnota;
-- totaliza a quantidade de itens já cadastrados na nota.
select count(*)
from item
where idnota = :lnota
into :ltotalitens;
-- verifica se existem mais do que 30 itens na nota.
if (ltotalitens > 30) then
begin
exception nota_cheia;
end
end

listagem 4. trigger para o postgre em pl/pgsql


create or replace function tr_in_item()
returns trigger as $tr_in_item$
declare ltotalitens int;
declare lnota int;
begin
-- seleciona o código da nota que está sendo incluída.
lnota := new.idnota;
-- totaliza a quantidade de itens já cadastrados na nota.
select into ltotalitens count(*)
from item
where idnota = lnota;
-- verifica se existem mais do que 30 itens na nota.
if (ltotalitens > 30) then
raise exception 'a nota % já está cheia. inclusão cancelada.', lnota;
end if;
return new;
end;
$tr_in_item$ language plpgsql;

-- exclui a trigger
drop trigger tr_in_item on item;

create trigger tr_in_item


before insert on item
for each row execute procedure tr_in_item();

listagem 5. trigger para o oracle


create or replace trigger tr_in_item
before insert
on item
for each row
declare ltotalitens integer;
lnota integer;
begin
-- seleciona o código da nota que está sendo incluída.
lnota := :new.idnota;
-- totaliza a quantidade de itens já cadastrados na nota.
select count(*)
into ltotalitens
from item
where idnota = lnota;
-- verifica se existem mais do que 30 itens na nota.
if (ltotalitens > 30) then
raise_application_error(-20000,'a nota ' || :new.idnota || ' já está cheia.
inclusão cancelada.');
end if;
end;

3. principais diferenças

1) recompilação da trigger
quando vamos compilar uma trigger o banco de dados deve excluir a versão
anterior caso ela já tenha sido compilada alguma vez.
para isto, no oracle e no postgres basta usar a declaração create or replace, já no
sql server é necessário verificar se já existe no dicionário um objeto com o mesmo
nome da trigger, e caso exista é explicitamente excluída a trigger com a instrução
abaixo:

if object_id ('tr_in_item','tr') is not null


drop trigger tr_in_item

o sql server também tem outra forma de atualizar uma trigger usando a instrução
alter trigger. neste exemplo a declaração da trigger começaria assim:
alter trigger tr_in_item
on item
after insert

a vantagem da instrução create or replace do oracle e do postgres é que se a


trigger não existe ela é criada, e já existindo ela é alterada.

2) declaração de variáveis locais


para o sql server o nome das variáveis locais deve sempre iniciar com uma
arroba (@).
outro detalhe importante é que os tipos de dados de cada sgbd podem ser
diferentes como varchar(sql server) e varchar2(oracle).

3) acesso aos dados alterados


nas triggers existe um conceito importante para realizarmos críticas e
procedimentos de controle: o acesso ao valor anterior e ao valor posterior de uma
coluna no momento em que a trigger foi disparada.
para termos acesso a estas informações no firebird e postgres, basta acrescentar o
prefixo old para consultar o valor anterior e new para o novo valor. no oracle é
similar, porém os prefixos são :old e :new.
no sql server já é bem diferente, existem duas tabelas: inserted e deleted. como o
nome diz em todas as inserções os dados serão carregados na tabela inserted,
assim como nas exclusões todos os dados estão armazenados na tabela deleted.
para as atualizações (update) o sql server considera como se ocorresse uma
exclusão seguida de uma inclusão, portanto os valores anteriores estão na tabela
deleted e os novos valores estão na tabela inserted.
para realizarmos procedimentos que usem estas tabelas, ou consultamos
diretamente elas ou carregamos os valores em variáveis locais, como foi feito no
exemplo deste artigo:
select @lnota = idnota
from inserted;

4) atribuição de variáveis no select


quando selecionamos o conteúdo de uma ou mais tabelas e queremos usar o
resultado da consulta em procedimentos posteriores a consulta, devemos carregar
o resultado dela em variáveis locais da trigger, nesta situação cada sgbd tratou de
uma forma:

4.1. sql server


select @ltotalitens = count(*)
from item
where idnota = @lnota;

4.2. firebird
select count(*)
from item
where idnota = :lnota
into :ltotalitens;

4.3. postgres
select into ltotalitens count(*)
from item
where idnota = lnota;

4.4. oracle
select count(*)
into ltotalitens
from item
where idnota = lnota;

5) mensagens
quando em uma trigger é necessário retornar alguma mensagem para a aplicação,
teremos formas diferentes nos sgbd

5.1 sql server

no sql server existe uma procedure que permite passarmos parâmetros para o
texto, no nosso exemplo o código da nota é substituído na mensagem na posição
onde temos a string “%d” que indica um parâmetro do tipo inteiro.

raiserror('a nota %d já está cheia. inclusão cancelada.',16,1,@lnota);

5.2 firebird

no firebird criamos uma exceção que no nosso exemplo chamamos de nota_cheia,


depois chamamos ela no corpo da trigger através do comando exception.

create exception nota_cheia 'a nota já está cheia. inclusão cancelada.';


exception nota_cheia;

5.3 postgres

o postgres tem uma forma similar de tratar as mensagens ao sql server, tem o
comando raise exception que permite colocarmos parâmetros na mensagem.

raise exception 'a nota % já está cheia. inclusão cancelada.', lnota;

5.4 oracle

para o oracle concatenamos o código da nota no meio da mensagem para termos o


mesmo efeito do sql server e o postgres. o primeiro parâmetro da procedure é o
código de erro, sendo que se for do número -20000 ao -20999 indica uma
mensagem texto.

raise_application_error(-20000,'a nota ' || :new.idnota || ' já está cheia. inclusão


cancelada.');

6) declaração da trigger no postgres


a declaração da trigger no postgres é bem diferente dos outros sgbd.
primeiro definimos um procedimento com o corpo da trigger:
create or replace function tr_in_item()
returns trigger as $tr_in_item$

e no final associamos este procedimento com a trigger através do código


plpgsql:

$tr_in_item$ language plpgsql;

-- exclui a trigger
drop trigger tr_in_item on item;

create trigger tr_in_item


before insert on item
for each row execute procedure tr_in_item();

conclusões
neste artigo vimos diferenças básicas que devem ser tratadas quando pretendemos
usar triggers para colocarmos as regras de negócio da aplicação em diversos sgbd.
existem outras tantas diferenças que devem ser consideradas, a idéia do artigo foi
somente tentar mostrar que ao criarmos procedimentos e funções para colocarmos
as regras do negócio no banco de dados, iremos nos deparar com linguagens bem
diferentes e nos obrigando a termos fontes distintos, que sofrerão manutenção
independente.

Das könnte Ihnen auch gefallen