Você está na página 1de 32

Utilizar rotinas de programação em Banco

de Dados

Apresentação
A manipulação de banco de dados às vezes necessita, além dos comandos básicos, da criação de
estruturas de códigos mais complexas e robustas. Exemplos dessas estruturas são os elementos
Procedure, Function e Triggers, que permitem a criação de blocos de códigos, com procedimentos,
parâmetros de entrada/saída, possibilitam trabalhar com os tipos de dados e disparar
procedimentos no banco de dados de forma reativa. Bons administradores de banco de dados
utilizam esses elementos diariamente a fim de garantir que processos sejam realizados
automaticamente, de forma adequada e organizada. Sendo assim, é fundamental entender estas
estruturas para conseguir realizar uma boa administração de banco de dados. Nesta Unidade de
Aprendizagem, você irá entender cada uma delas, conhecer alguns exemplos e identificar quando
aplicá-las adequadamente.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Reconhecer o conceito de rotinas de programação ETL.


• Definir os componentes de rotinas (Procedure, Function e Triggers).
• Identificar práticas de rotinas de manutenção.
Desafio
Você trabalha em uma empresa de cosméticos e o banco de dados da sua empresa possui
aproximadamente 150 tabelas, entre elas a tabela Produto. Todos os dias, o sistema emite um
relatório dos últimos produtos inseridos. Logo, uma consulta é realizada fazendo a ordenação dos
produtos adicionados no dia de forma decrescente pela coluna data_criacao.

No entanto, o sistema é utilizado por diversas filiais que rodam instâncias separadas do sistema e,
uma possível alteração na tabela implicaria em uma alteração no código de cada uma dessas filiais.

Como forma de facilitar a manutenção desse sistema, você poderia criar uma procedure para
executar o seguinte comando SQL.

SELECT nome, descricao, valor FROM produto WHERE DATE(data_criacao)=CURDATE() ORDER


BY data_criacao DESC

No comando, é realizado um SELECT pedindo os valores de nome, descrição e valor da tabela


Produto. A ordenação é feita pela data de criação de forma descentende.

Também é preciso limitar, com a cláusula WHERE, para retornar apenas os registros criados no dia
(com CURDATE()).

Sua missão é:
1 - Criar e descrever o comando de uma PROCEDURE que consulta todos os produtos da tabela
‘Produto’ ordenando pela data de criação em ordem decrescente.
2 - Descrever o comando para executar esta procedure.
3 - Modificar a procedure criada para pesquisar por produtos pela coluna nome.
4 - Descrever como ficaria o comando para executar a procedure do item 3, buscando pelo produto
“batom”.
Infográfico
Veja, no Infográfico a seguir, as rotinas de banco de dados que, entre outras vantagens, permitem a
automatização de processos diretamente no SGBD.
Conteúdo do livro
No trecho Procedimentos do PL/SQL, da obra Projeto, Desenvolvimento de Aplicações &
Administração de Banco de Dados, você aprenderá, a partir de exemplos, sobre os procedimentos no
PL/SQL, que são blocos nomeados com um conjunto operacional de parâmetros.

Boa leitura.
TRADUÇÃO DA

TERCEIRA EDIÇÃO

PROJETO, DESENVOLVIMENTO DE APLICAÇÕES


& ADMINISTRAÇÃO DE BANCO DE DADOS

Michael V. Mannino
M284p Mannino, Michael V.
Projeto, desenvolvimento de aplicações e administração
de banco de dados [recurso eletrônico] / Michael V.
Mannino ; tradução: Beth Honorato ... [et al.] ; revisão
técnica: Antônio Fernandes Nunes Guardado, Sidney da
Silva Viana. – 3. ed. – Dados eletrônicos. – Porto Alegre:
AMGH, 2014.

Editado também como livro impresso em 2008.


ISBN 978-85-8055-363-5

1. Banco de dados – Gerenciamento – Programas de


computador. 2. Projeto de banco de dados. 3. Software de
aplicativos – Desenvolvimento. I. Título.

CDD 005.74
CDU 004.658

Catalogação na publicação: Ana Paula M. Magnus – CRB 10/2052


Capítulo 11 Procedimentos Armazenados e Gatilhos 389

Esta seção trata dos pacotes, das funções e dos procedimentos em PL/SQL. Algumas
partes adicionais do PL/SQL (cursores e exceções) são apresentadas para demonstrar a utili-
dade dos procedimentos armazenados. Os scripts de teste pressupõem o preenchimento das
tabelas da universidade, conforme os dados no site do livro.

11.2.1 Procedimentos do PL/SQL


No PL/SQL, procedimento é um bloco nomeado com um conjunto opcional de parâmetros.
Cada parâmetro contém um nome de parâmetro, um uso (IN, OUT, IN OUT) e um tipo de
dado. Um parâmetro de entrada (IN) não pode ser modificado dentro de um procedimento.
Um parâmetro de saída (OUT) recebe um valor dentro de um procedimento. Um parâmetro
de entrada–saída (IN OUT) deve receber um valor fora de um procedimento, mas pode ser
modificado dentro de um procedimento. A especificação de tipo de dado não deve incluir ne-
nhuma restrição, como, por exemplo, de comprimento. Por exemplo, para parâmetro de uma
cadeia de caracteres deve-se utilizar o tipo de dado VARCHAR2. Não se deve fornecer um
comprimento na especificação do tipo de dado para um parâmetro.

Estrutura de Procedimento:
CREATE [OR REPLACE] PROCEDURE NomeProcedimento
[ ( Parâmetro1, . . . , ParâmetroN ) ]
IS
[ seqüência de declarações ]
BEGIN
seqüência de instruções
[ EXCEPTION
seqüência de instruções para responder às exceções ]
END;

Em um exemplo simples, o procedimento pr_IncluirRegistro do Exemplo 11.13 insere


uma linha na tabela Registro do banco de dados de uma universidade. Os parâmetros de entrada
fornecem os valores a serem inseridos. O procedimento de chamada Dbms_Output.Put_Line
exibe uma mensagem confirmando o sucesso da inserção. No código de teste que segue a ins-
trução CREATE PROCEDURE, a instrução ROLLBACK elimina o efeito de qualquer
instrução do SQL. As instruções ROLLBACK são úteis no código de teste quando as modifi-
cações feitas no banco de dados não devem ser permanentes.

EXEMPLO 11.13 Procedimento para Inserir uma Linha na Tabela Registro Acompanhado de
Código para Testar o Procedimento
CREATE OR REPLACE PROCEDURE pr_IncluirRegistro
(vNumReg IN Registro.NumReg%TYPE,
vCPFAluno IN Registro.CPFAluno%TYPE,
vSituacaoReg IN Registro.SituacaoReg%TYPE,
vDataReg IN Registro.DataReg%TYPE,
vTrimestreReg IN Registro.TrimestreReg%TYPE,
vAnoReg IN Registro.AnoReg%TYPE) IS
-- Cria um novo registro
BEGIN
INSERT INTO Registro
(NumReg, CPFAluno, SituacaoReg, DataReg, TrimestreReg, AnoReg)
VALUES (vNumReg, vCPFAluno, vSituacaoReg, vDataReg, vTrimestreReg,
vAnoReg);
390 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

dbms_output.put_line('Adicionada uma linha na tabela Registro');


END;
/
-- Código de teste
SET SERVEROUTPUT ON;
-- Número de linhas antes da execução do procedimento
SELECT COUNT(*) FROM Registro;
BEGIN
pr_IncluirRegistro
(1275,'901-23-4567','F',To_Date('27-Fev-2006'), 'Primavera', 2006);
END;
/
-- Número de linhas depois da execução do procedimento
SELECT COUNT(*) FROM Registro;
-- Exclui a linha inserida utilizando a instrução ROLLBACK
ROLLBACK;

Para permitir que outros procedimentos reutilizem pr_IncluirRegistro, é necessário


substituir a visualização de saída por um parâmetro de saída indicando o sucesso ou a falha
da inserção. O Exemplo 11.14 modifica o Exemplo 11.13 para utilizar um parâmetro de saída.
A exceção OTHERS identifica diversos erros, como a violação de uma restrição de chave
primária ou de uma restrição de chave estrangeira. A exceção OTHERS deve ser utilizada
quando não há necessidade de código especializado para cada tipo de exceção. Para identi-
ficar um erro específico, deve-se utilizar uma exceção predefinida (Tabela 11.4) ou criar uma
exceção definida pelo usuário. A última seção contém um exemplo de exceção definida pelo
usuário. Depois do procedimento, o script inclui casos para testar se houve uma inserção com
sucesso e violação de uma chave primária.

EXEMPLO 11.14 Procedimento para Inserir uma Linha na Tabela Registro Acompanhada de
Código para Testar o Procedimento
CREATE OR REPLACE PROCEDURE pr_IncluirRegistro
(vNumReg IN Registro.NumReg%TYPE,
vCPFAluno IN Registro.CPFAluno%TYPE,
vSituacaoReg IN Registro.SituacaoReg%TYPE,
vDataReg IN Registro.DataReg%TYPE,
vTrimestreReg IN Registro.TrimestreReg%TYPE,
vAnoReg IN Registro.AnoReg%TYPE,
vResultado OUT BOOLEAN ) IS
-- Cria um novo registro
-- vResultado é TRUE se completado com sucesso, do contrário, é falso.
BEGIN
vResultado := TRUE;
INSERT INTO Registro
(NumReg, CPFAluno, SituacaoReg, DataReg, TrimestreReg, AnoReg)
VALUES (vNumReg, vCPFAluno, vSituacaoReg, vDataReg, vTrimestreReg,
vAnoReg);
Capítulo 11 Procedimentos Armazenados e Gatilhos 391

EXCEPTION
WHEN OTHERS THEN vResultado := FALSE;
END;
/
-- Código de teste
SET SERVEROUTPUT ON;
-- Número de linhas antes da execução do procedimento
SELECT COUNT(*) FROM Registro;
DECLARE
-- Parâmetro de saída deve ser declarado no bloco de chamada
Resultado BOOLEAN;
BEGIN
-- Esse teste deve se completado com sucesso.
-- O procedimento atribui valor ao parâmetro de saída (Resultado).
pr_IncluirRegistro
(1275,'901-23-4567','F',To_Date('27-Fev-2006'),'Primavera',2006,Resultado);
IF Resultado THEN
dbms_output.put_line('Adicionada uma linha na tabela Registro');
ELSE
dbms_output.put_line('Linha não adicionada na tabela Registro');
END IF;
-- Esse teste deve falhar por causa da chave primária em duplicidade.
pr_IncluirRegistro
(1275,'901-23-4567','F',To_Date('27-Fev-2006'), 'Primavera',2006,Resultado);
IF Resultado THEN
dbms_output.put_line('Adicionada uma linha na tabela Registro');
ELSE
dbms_output.put_line('Linha não adicionada na tabela Registro');
END IF;
END;
/
-- Número de linhas depois das execuções do procedimento
SELECT COUNT(*) FROM Registro;
-- Exclui a linha inserida
ROLLBACK;

TABELA 11.4
Exceção Quando Acionada
Lista de Exceções
Predefinidas Comuns Cursor_Already_Open Tenta abrir um cursor já aberto anteriormente
do PL/SQL Dup_Val_On_Index Tenta armazenar um valor em duplicidade em um índice único
Invalid_Cursor Tenta executar uma operação inválida em um cursor, como, por
exemplo, fechar um cursor não aberto anteriormente
No_Data_Found A instrução SELECT INTO não retorna nenhuma linha
Rowtype_Mismatch Tenta designar valores com tipos de dados incompatíveis entre um
cursor e uma variável
Timeout_On_Resource Tempo limite, por exemplo, quando se aguarda por um bloqueio exclusivo1
Too_Many_Rows A instrução SELECT INTO retorna mais de uma linha

1
No Capítulo 15, será explicado o uso de tempo-limite com bloqueio de transação para evitar deadlocks.
392 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

11.2.2 Funções do PL/SQL


procedimentos versus As funções devem retornar valores em vez de manipular variáveis de saída e causar efeitos
funções colaterais como inserir linhas em uma tabela. Sempre se deve utilizar um procedimento
utilizar um procedimento quando se deseja ter mais de um resultado e/ou um efeito colateral. As funções devem ser
se o código deve ter mais utilizadas em expressões, significando que uma chamada de função pode ser substituída
de um resultado ou um efeito pelo valor que ela retorna. Uma função do PL/SQL é semelhante a um procedimento, no
colateral. As funções devem
ser utilizadas em expressões,
sentido de que ambos contêm uma lista de parâmetros. No entanto, a função deve utilizar so-
significando que uma chamada mente parâmetros de entrada. Depois da lista de parâmetros, o tipo de dado de retorno é
de função pode ser substituída definido sem nenhuma restrição, como, por exemplo, de comprimento. No corpo da função,
pelo valor que ela retorna. a seqüência de instruções deve incluir uma instrução RETURN para gerar o valor de saída
Para permitir a utilização da função.
de funções em expressões,
as funções devem utilizar
somente parâmetros de entrada. Estrutura da Função:
CREATE [OR REPLACE] FUNCTION NomeFuncao
[ (Parâmetro1, . . . , ParâmetroN) ]
RETURN TipoDado
IS
[ seqüência de declarações ]
BEGIN
seqüência de instruções incluindo uma instrução RETURN
[ EXCEPTION
seqüência de instruções para responder às exceções]
END;

Em um exemplo simples, a função fn_RecuperarNomeAluno do Exemplo 11.15 recu-


pera o nome de um aluno dado o número do CPF. A exceção predefinida No_Data_Found é
verdadeira se a instrução SELECT não retornar pelo menos uma linha. A instrução SELECT
utiliza a cláusula INTO para associar as variáveis às colunas do banco de dados. A cláusula
INTO pode ser utilizada somente quando a instrução SELECT retorna no máximo uma
linha. Se uma cláusula INTO for utilizada quando a instrução SELECT retornar mais de
uma linha, é gerada uma exceção. O procedimento Raise_Application_Error mostra uma
mensagem de erro e um número de erro. Esse procedimento predefinido é útil para manipu-
lar erros inesperados.

EXEMPLO 11.15 Função para Recuperar o Nome do Aluno Dado seu Número do CPF
CREATE OR REPLACE FUNCTION fn_RecuperarNomeAluno
(vCPFAluno IN Aluno.CPFAluno%type) RETURN VARCHAR2 IS
-- Recupera o nome do aluno (concatena o nome e sobrenome)
-- dado o número do CPF do aluno. Se o aluno não existe,
-- retorna nulo.
vNomeAluno Aluno.NomeAluno%TYPE;
vSobrenomeAluno Aluno.SobrenomeAluno%TYPE;
BEGIN
SELECT NomeAluno, SobrenomeAluno
INTO vNomeAluno, vSobrenomeAluno
FROM Aluno
WHERE CPFAluno = vCPFAluno;
RETURN(vSobrenomeAluno || ', ' || vNomeAluno);
Capítulo 11 Procedimentos Armazenados e Gatilhos 393

EXCEPTION
-- No_Data_Found é disparado se a instrução SELECT não retorna nenhum dado.
WHEN No_Data_Found THEN
RETURN(NULL);
WHEN OTHERS THEN
raise_application_error(-20001, 'Database error');
END;
/
-- Código de teste
SET SERVEROUTPUT ON;
DECLARE
vNomeAluno VARCHAR2(50);
BEGIN
-- Essa chamada deve mostrar o nome de um aluno.
vNomeAluno := fn_RecuperarNomeAluno('901-23-4567');
IF vNomeAluno IS NULL THEN
dbms_output.put_line('Aluno não encontrado');
ELSE
dbms_output.put_line('Nome é ' || vNomeAluno);
END IF;
-- Essa chamada não deve mostrar o nome de um aluno.
vNomeAluno := fn_RecuperarNomeAluno('905-23-4567');
IF vNomeAluno IS NULL THEN
dbms_output.put_line('Aluno não encontrado');
ELSE
dbms_output.put_line('Nome é ' || vNomeAluno);
END IF;
END;
/

O Exemplo 11.16 mostra uma função com uma consulta mais complexa que a função do
Exemplo 11.15. O código de teste contém dois casos para testar, procurando um aluno exis-
tente e um aluno não existente, juntamente com a instrução SELECT que utiliza a função no
resultado. Uma vantagem importante das funções é que elas podem ser utilizadas em ex-
pressões nas instruções SELECT.

EXEMPLO 11.16 Função para Computar a Média de Notas Ponderada Dados o Número do CPF
e o Ano do Aluno
CREATE OR REPLACE FUNCTION fn_CalcularMediaPonderada
(vCPFAluno IN Aluno.CPFAluno%TYPE, vAno IN Oferecimento.AnoOfer%TYPE)
RETURN NUMBER IS
-- Computa a média de notas ponderada dados o ano e o número do CPF do
aluno.
-- Média ponderada é a soma das cargas horárias multiplicada pela nota
-- dividida por cargas horárias totais.
-- Se o aluno não existe, retorna nulo.
MediaPonderada NUMBER;
394 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

BEGIN
SELECT SUM (NotaMatr*CargaHoraCurso) / SUM(CargaHoraCurso)
INTO MediaPonderada
FROM Aluno, Registro, Matricula, Oferecimento, Curso
WHERE Aluno.CPFAluno = vCPFAluno
AND Oferecimento.AnoOfer = vAno
AND Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
AND Matricula.NumOfer = Oferecimento.NumOfer
AND Oferecimento.NumCurso = Curso.NumCurso;
RETURN(MediaPonderada);
EXCEPTION
WHEN No_Data_Found THEN
RETURN(NULL);
WHEN OTHERS THEN
raise_application_error(-20001, 'Database error');
END;
/
-- Código de teste
SET SERVEROUTPUT ON;
DECLARE
vMedia DECIMAL(3,2);
BEGIN
-- Essa chamada deve mostrar uma média ponderada.
vMedia := fn_CalcularMediaPonderada('901-23-4567', 2006);
IF vMedia IS NULL THEN
dbms_output.put_line ('Aluno ou Matrículas não encontrados');
ELSE
dbms_output.put_line('Média ponderada é' || to_char(vMedia));
END IF;
-- Essa chamada não deve mostrar uma média ponderada.
vMedia := fn_CalcularMediaPonderada('905-23-4567', 2006);
IF vMedia IS NULL THEN
dbms_output.put_line('Aluno ou Matrículas não encontrados');
ELSE
dbms_output.put_line('Média Ponderada é' || to_char(vMedia));
END IF;
END;
/
-- Utiliza a função em uma consulta
SELECT CPFAluno, NomeAluno, SobrenomeAluno,
fn_CalcularMediaPonderada(CPFAluno, 2006) AS MediaPonderada
FROM Aluno;
Capítulo 11 Procedimentos Armazenados e Gatilhos 395

11.2.3 Utilização de Cursores


cursor PL/SQL implícito Os procedimentos e as funções apresentados anteriormente são bem simples, já que envolvem
um cursor não é declarado a recuperação de uma única linha. Funções e procedimentos mais complexos envolvem itera-
nem aberto explicitamente. ção em múltiplas linhas utilizando um cursor. O PL/SQL fornece declaração (explícita ou
Em vez disso, uma versão
implícita) de cursor, instrução FOR especializada para iteração de cursor, atributos de cursor
especial da instrução FOR
declara, abre, itera e fecha para indicar o status de operações do cursor e instruções para executar ações em cursores
uma instrução SELECT explícitos. O PL/SQL tem suporte a cursores estáticos, em que a instrução do SQL é co-
nomeada localmente. Um nhecida em tempo de compilação, além de cursores dinâmicos, em que a instrução do SQL
cursor implícito não pode ser não é determinada até o tempo de execução.
referenciado fora da instrução O Exemplo 11.17 descreve um cursor implícito para retornar a classificação de um aluno
FOR no qual ele é declarado. em um oferecimento. Os cursores implícitos não são declarados na seção DECLARE. Ao
contrário, os cursores implícitos são declarados, abertos e iterados dentro de uma instrução
FOR. No Exemplo 11.17, a instrução FOR é iterada para cada linha da instrução SELECT,
utilizando o cursor implícito RegMatr. A instrução SELECT classifica o resultado em ordem
decrescente, por nota de matrícula. A função sai da instrução FOR quando o valor CPFAluno
corresponde com o valor do parâmetro. A classificação é incrementada somente quando a
nota muda de forma que dois alunos com a mesma nota têm a mesma classificação.

EXEMPLO 11.17 Utilização de um Cursor Implícito para Determinar a Classificação na Turma de


Determinado Aluno e Oferecimento
CREATE OR REPLACE FUNCTION fn_DeterminarClassificação
(vCPFAluno IN Aluno.CPFAluno%TYPE, vNumOfer IN
Oferecimento.NumOfer%TYPE)
RETURN INTEGER IS
-- Determina a classificação de um determinado número do CPF de aluno e
NumOfer.
-- Utiliza um cursor implícito.
-- Se o aluno ou o oferecimento não existe, retorna 0.
ClassTemp INTEGER :=0;
PrevNotaMatr Matricula.NotaMatr%TYPE := 9,9;
ENCONTRADO BOOLEAN := FALSE;
BEGIN
-- Laço por meio de cursor implícito
FOR RegMatr IN
( SELECT Aluno.CPFAluno, NotaMatr
FROM Aluno, Registro, Matricula
WHERE Matricula.NumOfer = vNumOfer
AND Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
ORDER BY NotaMatr DESC ) LOOP
IF RegMatr.NotaMatr < PrevNotaMatr THEN
-- Incrementa a classificação quando a nota muda
ClassTemp := ClassTemp + 1;
PrevNotaMatr := RegMatr.NotaMatr;
END IF;
IF RegMatr.CPFAluno = vCPFAluno THEN
Encontrado := TRUE;
EXIT;
END IF;
END LOOP;
396 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

IF Encontrado THEN
RETURN(ClassTemp);
ELSE
RETURN(0);
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001, 'Database error');
END;
/
-- Código de teste
SET SERVEROUTPUT ON;
-- Executa uma consulta para ver os dados de teste
SELECT Aluno.CPFAluno, NotaMatr
FROM Aluno, Registro, Matricula
WHERE Matricula.NumOfer = 5679
AND Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
ORDER BY NotaMatr DESC;
-- Script de teste
DECLARE
vClass INTEGER;
BEGIN
-- Essa chamada deve retornar uma classificação de 6.
vClass := fn_DeterminarClassificacao('789-01-2345', 5679);
IF vClass > 0 THEN
dbms_output.put_line('Classificação é' || to_char(vClass));
ELSE
dbms_output.put_line('Aluno não está matriculado.');
END IF;
-- Essa chamada deve retornar uma classificação de 0.
vClass := fn_DeterminarClassificacao('789-01-2005', 5679);
IF vClass > 0 THEN
dbms_output.put_line('Classificação é' || to_char(vClass));
ELSE
dbms_output.put_line('Aluno não está matriculado.');
END IF;
END;
/

cursor PL/SQL explícito


um cursor declarado com
a instrução CURSOR na seção O Exemplo 11.18 descreve um procedimento com um cursor explícito para retornar
DECLARE. Cursores explícitos a classificação e a nota de um aluno em um oferecimento. O cursor explícito CursorMatr na
são manipulados geralmente instrução CURSOR contém a quantidade do oferecimento como parâmetro. Os cursores
pelas instruções OPEN, CLOSE explícitos devem utilizar parâmetros para valores não constantes de busca na instrução
e FETCH. Cursores explícitos
SELECT associada. As instruções OPEN, FETCH e CLOSE substituem a instrução FOR do
podem ser referenciados em
qualquer lugar dentro da seção Exemplo 11.17. Depois da instrução FETCH, a condição CursorMatr%NotFound testa bus-
BEGIN. cando o cursor vazio.
Capítulo 11 Procedimentos Armazenados e Gatilhos 397

EXEMPLO 11.18 Utilização de um Cursor Explícito para Determinar a Classificação e a Nota de


Determinado Aluno e Oferecimento
CREATE OR REPLACE PROCEDURE pr_DeterminarClassificacao
(vCPFAluno IN Aluno.CPFAluno%TYPE, vNumOfer IN
Oferecimento.NumOfer%TYPE,
ClassExt OUT INTEGER, NotaExt OUT Matricula.NotaMatr%TYPE ) IS
-- Determina a classificação e a nota de um determinado número do CPF de aluno
-- e NumOfer utilizando um cursor explícito.
-- Se o aluno ou o oferecimento não existe, retorna 0.
ClassTemp INTEGER :=0;
PrevNotaMatr Matricula.NotaMatr%TYPE := 9,9;
Encontrado BOOLEAN := FALSE;
NotaTemp Matricula.NotaMatr%TYPE;
CPFAlunoTemp Aluno.CPFAluno%TYPE;
-- Cursor explícito
CURSOR CursorMatr (NumOferTemp Oferecimento.NumOfer%TYPE) IS
SELECT Aluno.CPFAluno, NotaMatr
FROM Aluno, Registro, Matricula
WHERE Matricula.NumOfer = vNumOfer
AND Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
ORDER BY NotaMatr DESC;
BEGIN
-- Abre e faz o laço por meio de um cursor explícito
OPEN CursorMatr(vNumOfer);
LOOP
FETCH CursorMatr INTO CPFAlunoTemp, NotaTemp;
EXIT WHEN CursorMatr%NotFound;
IF NotaTemp < PrevNotaMatr THEN
-- Incrementa a classificação quando a nota muda
ClassTemp := ClassTemp + 1;
PrevNotaMatr := NotaTemp;
END IF;
IF CPFAlunoTemp = vCPFAluno THEN
Encontrado := TRUE;
EXIT;
END IF;
END LOOP;
CLOSE CursorMatr;
IF Encontrado THEN
ClassExt := ClassTemp;
NotaExt := PrevNotaMatr;
ELSE
ClassExt := 0;
NotaExt := 0;
END IF;
398 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001, 'Database error');
END;
/
-- Código de teste
SET SERVEROUTPUT ON;
-- Executa uma consulta para ver os dados de teste
SELECT Aluno.CPFAluno, NotaMatr
FROM Aluno, Registro, Matricula
WHERE Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
AND Matricula.NumOfer = 5679
ORDER BY NotaMatr DESC;
-- Script de teste
DECLARE
vClass INTEGER;
vNota Matricula.NotaMatr%TYPE;
BEGIN
-- Essa chamada deve produzir uma classificação de 6.
pr_DeterminarClassificacao('789-01-2345', 5679, vClass, vNota);
IF vClass > 0 THEN
dbms_output.put_line('Classificação é' || to_char(vClass) || '.');
dbms_output.put_line('Nota é ' || to_char(vNota) || '.');
ELSE
dbms_output.put_line('Aluno não está matriculado.');
END IF;
-- Essa chamada deve produzir uma classificação de 0.
pr_DeterminarClassificacao('789-01-2005', 5679, vClass, vNota);
IF vClass > 0 THEN
dbms_output.put_line('Classificação é' || to_char(vClass) || '.');
dbms_output.put_line('Nota é ' || to_char(vNota) || '.');
ELSE
dbms_output.put_line('Aluno não está matriculado.');
END IF;
END;
/

O PL/SQL oferece suporte a inúmeros atributos de cursor, assim como mostra a Tabela
11.5. Quando utilizado com um cursor explícito, o nome do cursor precede o atributo de cur-
sor. Quando utilizado com um cursor implícito, a palavra-chave do SQL precede o atributo de
cursor. Por exemplo, SQL%RowCount denota a quantidade de linhas em um cursor implícito.
O nome do cursor implícito não é utilizado.

11.2.4 Pacotes do PL/SQL


Os pacotes fornecem suporte a uma unidade de modularidade maior que os procedimentos ou
as funções. Um pacote pode conter procedimentos, funções, exceções, variáveis, constantes,
tipos e cursores. Agrupando os objetos relacionados, fica mais fácil reutilizar um pacote do
Capítulo 11 Procedimentos Armazenados e Gatilhos 399

TABELA 11.5
Atributo de Cursor Valor
Lista de Atributos
Comuns de Cursor %IsOpen Verdadeiro se o cursor estiver aberto
%Found Verdadeiro se o cursor não estiver vazio depois de uma instrução FETCH
%NotFound Verdadeiro se o cursor estiver vazio depois de uma instrução FETCH
%RowCount Número de linhas recuperadas. Depois de cada FETCH, RowCount é
incrementado.

que procedimentos e funções individuais. O Oracle oferece muitos pacotes predefinidos,


como o pacote DBMS_Output contendo grupos de objetos relacionados. Além disso, o pacote
separa a implementação de uma interface pública da privada para apoiar esforços de
manutenção de software. As mudanças em uma implementação privada não afetam o uso
de um pacote por meio de sua interface. O Capítulo 18, sobre banco de dados orientados a
objeto, apresenta informações mais detalhadas sobre as vantagens das unidades maiores de
modularidade.
A interface de um pacote contém as definições de procedimentos e funções juntamente
com outros objetos que podem ser especificados na seção DECLARE de um bloco do
PL/SQL. Todos os objetos da interface de um pacote são públicos. O Exemplo 11.19 mostra
a interface de um pacote combinando alguns procedimentos e funções apresentados nas
seções anteriores.

Estrutura da Interface de um Pacote:


CREATE [OR REPLACE] PACKAGE NomePacote IS
[ Declarações de constante, variável e tipo]
[ Declarações de cursor ]
[ Declarações de exceção ]
[ Definições de procedimento ]
[ Definições de função ]
ENDNomePacote;

EXEMPLO 11.19 Interface de Pacote Contendo Procedimentos e Funções Relacionados do Banco


de Dados de uma Universidade

CREATE OR REPLACE PACKAGE pck_Universidade IS


PROCEDURE pr_DeterminarClassificação
(vCPFAluno IN Aluno.CPFAluno%TYPE, vNumOfer IN
Oferecimento.NumOfer%TYPE,
ClassExt OUT INTEGER, NotaExt OUT Matricula.NotaMatr%TYPE );
FUNCTION fn_CalcularMédiaPonderada
(vCPFAluno IN Aluno.CPFAluno%TYPE, vAno IN Oferecimento.AnoOfer%TYPE)
RETURN NUMBER;
END pck_Universidade;
/

O corpo ou a implementação de um pacote contém detalhes privados de um pacote. Para


cada objeto na interface do pacote, o corpo do pacote deve definir uma implementação. Além
disso, os objetos privados podem ser definidos no corpo de um pacote. Os objetos privados
podem ser utilizados somente dentro do corpo de um pacote. Usuários externos de um pacote
não podem acessar objetos privados. O Exemplo 11.20 mostra o corpo da interface do paco-
te do Exemplo 11.19. Observe que cada procedimento ou função termina com uma instrução
END contendo o nome do procedimento ou da função. Do contrário, as implementações do
procedimento ou da função seriam idênticas à criação de um procedimento ou de uma função
fora de um pacote.
400 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

Estrutura do Corpo de um Pacote:


CREATE [OR REPLACE] PACKAGE BODY NomePacote IS
[ Declarações de variável e tipo]
[ Declarações de cursor ]
[ Declarações de exceção ]
[ Implementações de procedimento ]
[ Implementações de função]
[ BEGIN seqüência de instruções ]
[ EXCEPTION instruções para tratar exceções ]
END NomePacote;

EXEMPLO 11.20 Corpo de Pacote Contendo Implementações de Procedimentos e Funções


CREATE OR REPLACE PACKAGE BODY pck_Universidade IS
PROCEDURE pr_DeterminarClassificacao
(vCPFAluno IN Aluno.CPFAluno%TYPE, vNumOfer IN Oferecimento.
NumOfer%TYPE,
ClassExt OUT INTEGER, NotaExt OUT Matricula.NotaMatr%TYPE ) IS
-- Determina a classificação e a nota de um determinado número do CPF de aluno
-- e NumOfer utilizando um cursor explícito.
-- Se o aluno ou o oferecimento não existe, retorna 0.
ClassTemp INTEGER :=0;
PrevNotaMatr Matricula.NotaMatr%TYPE := 9,9;
Encontrado BOOLEAN := FALSE;
NotaTemp Matricula.NotaMatr%TYPE;
CPFAlunoTemp Aluno.CPFAluno%TYPE;
-- Cursor explícito
CURSOR CursorMatr (NumOferTemp Oferecimento.NumOfer%TYPE) IS
SELECT Aluno.CPFAluno, NotaMatr
FROM Aluno, Registro, Matricula
WHERE Matricula.NumOfer = vNumOfer
AND Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
ORDER BY NotaMatr DESC;
BEGIN
-- Abre e faz o laço por meio de um cursor explícito
OPEN CursorMatr(vNumOfer);
LOOP
FETCH CursorMatr INTO CPFAlunoTemp, NotaTemp;
EXIT WHEN CursorMatr%NotFound;
IF NotaTemp < PrevNotaMatr THEN
-- Incrementa a classificação quando a nota muda
ClassTemp := ClassTemp + 1;
PrevNotaMatr := NotaTemp;
END IF;
IF CPFAlunoTemp = vCPFAluno THEN
Encontrado := TRUE;
EXIT;
END IF;
END LOOP;
Capítulo 11 Procedimentos Armazenados e Gatilhos 401

CLOSE CursorMatr;
IF Encontrado THEN
ClassExt := ClassTemp;
NotaExt := PrevNotaMatr;
ELSE
ClassExt := 0;
NotaExt := 0;
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001, 'Database error');
END pr_DeterminarClassificacao;
FUNCTION fn_CalcularMediaPonderada
(vCPFAluno IN Aluno.CPFAluno%TYPE, vAno IN Oferecimento.AnoOfer%TYPE)
RETURN NUMBER IS
-- Computa a média ponderada dado o número do CPF de um aluno e o ano.
-- Média ponderada é a soma das cargas horárias multiplicada pela nota
-- dividida pelas cargas horárias totais.
-- Se o aluno não existe, retorna nulo.
MediaPonderada NUMBER;
BEGIN
SELECT SUM(NotaMatr*CargaHoraCurso)/SUM(CargaHoraCurso)
INTO MediaPonderada
FROM Aluno, Registro, Matricula, Oferecimento, Curso
WHERE Aluno.CPFAluno = vCPFAluno
AND Oferecimento.AnoOfer = vAno
AND Aluno.CPFAluno = Registro.CPFAluno
AND Registro.NumReg = Matricula.NumReg
AND Matricula.NumOfer = Oferecimento.NumOfer
AND Oferecimento.NumCurso = Curso.NumCurso;
RETURN(MediaPonderada);
EXCEPTION
WHEN no_data_found THEN
RETURN(NULL);
WHEN OTHERS THEN
raise_application_error(-20001, 'Database error');
END fn_CalcularMediaPonderada;
END pck_Universidade;
/

Para utilizar os objetos de um pacote, é necessário utilizar o nome do pacote antes do


nome do objeto. No Exemplo 11.21, é possível observar que o nome do pacote (pck_Univer-
sidade) precede os nomes de procedimento e função.
402 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

EXEMPLO 11.21 Script para Utilizar os Procedimentos e as Funções do Pacote da Universidade


SET SERVEROUTPUT ON;
DECLARE
vClass INTEGER;
vNota Matricula.NotaMatr%TYPE;
vMedia NUMBER;
BEGIN
-- Essa chamada deve produzir uma classificação de 6.
pck_Universidade.pr_DeterminarClassificacao('789-01-2345', 5679, vClass, vNota);
IF vClass > 0 THEN
dbms_output.put_line('Classificação é' || to_char(vClass) || '.');
dbms_output.put_line('Nota é ' || to_char(vNota) || '.');
ELSE
dbms_output.put_line('Aluno não está matriculado.');
END IF;
-- Essa chamada deve mostrar uma média ponderada.
vMedia := pck_Universidade.fn_CalcularMediaPonderada('901-23-4567', 2006);
IF vMedia IS NULL THEN
dbms_output.put_line('Aluno ou Matrículas não encontrados');
ELSE
dbms_output.put_line('Média Ponderada é' || to_char(vMedia));
END IF;
END;
/

11.3 Gatilhos
Gatilhos (triggers) são regras gerenciadas por um SGBD. Como o gatilho envolve um evento,
gatilho uma condição e uma seqüência de ações, ele também é conhecido como regra evento-con-
uma regra que é armazenada
e executada por um SGBD.
dição-ação. A escrita da parte de ação ou do corpo do gatilho é semelhante à escrita de um
Como um gatilho envolve procedimento ou uma função, exceto que o gatilho não possui parâmetros. Os gatilhos são
um evento, uma condição executados pelo sistema de regras do SGBD e não por chamadas explícitas como nos pro-
e uma seqüência de ações, cedimentos e nas funções. Os gatilhos tornaram-se oficialmente parte do SQL:1999 embora
também é conhecido como a maioria dos fornecedores de SGBD houvesse implementado gatilhos bem antes do lança-
regra evento-condição-ação. mento do SQL:1999.
Esta seção trata dos gatilhos do Oracle com noções de gatilhos do SQL:2003. Na
primeira parte desta seção, serão discutidas as razões por que os gatilhos são parte importante
do desenvolvimento de aplicações de banco de dados e será introduzida a classificação dos
gatilhos. Na segunda parte, será mostrada a codificação de gatilhos em PL/SQL. Na parte
final, serão apresentados os procedimentos de execução de gatilhos do Oracle e SQL:2003.

11.3.1 Motivação e Classificação dos Gatilhos


Os gatilhos são amplamente implementados nos SGBDs em virtude dos vários usos em apli-
cações de negócios. A lista a seguir explica os usos típicos dos gatilhos.
• Restrições de integridade complexas: restrições de integridade que não podem ser es-
pecificadas por restrições nas instruções CREATE TABLE. Uma restrição típica nas ins-
truções CREATE TABLE é a que impossibilita referenciar colunas de outras tabelas. Os
gatilhos permitem referenciar colunas de múltiplas tabelas para suplantar essa limitação.
Capítulo 11 Procedimentos Armazenados e Gatilhos 403

Uma alternativa de gatilho para uma restrição complexa é a asserção discutida no Capí-
tulo 14. No entanto, a maioria dos SGBDs não oferece suporte a asserções, assim, os
gatilhos são a única escolha para restrições de integridade complexas.
• Restrições de transição: restrições de integridade que comparam os valores antes e de-
pois de uma atualização. Por exemplo, pode-se escrever um gatilho para impor uma res-
trição de transição, limitando os aumentos salariais em, no máximo, 10%.
• Propagação de atualização: colunas derivadas de atualização em tabelas relacionadas,
por exemplo, para manter o saldo de estoque permanente ou os assentos livres em um
vôo programado.
• Relatório de exceção: cria um registro de condições incomuns como alternativa para re-
jeitar uma transação. O gatilho também pode enviar uma notificação em uma mensagem
de correio eletrônico. Por exemplo, em vez de rejeitar o aumento salarial de 10%, o gatilho
pode criar um registro de exceção e notificar o gestor para rever o aumento salarial.
• Trilha de auditoria: cria o registro histórico de uma transação, por exemplo, o histórico
de utilização do caixa automático.
O SQL:2003 classifica os gatilhos por granularidade, momento de disparo e evento
aplicável. Em termos de granularidade, o gatilho pode envolver cada linha afetada por uma
instrução do SQL ou uma instrução inteira do SQL. Os gatilhos de linha são mais comuns que
os de instrução. Em termos de momento de disparo, o gatilho pode ser disparado antes ou de-
pois do evento. Normalmente, os gatilhos para verificação de restrições são disparados antes
de um evento, enquanto os gatilhos de atualização de tabelas e execução de outras ações são
disparados depois de um evento. Em termos de evento aplicável, o gatilho pode ser aplicado
a instruções INSERT, UPDATE e DELETE. Os gatilhos de atualização podem especificar
uma lista de colunas aplicáveis.
Como a especificação de gatilhos do SQL:1999 foi definida em resposta às implemen-
tações dos fornecedores, a maioria das implementações de gatilho varia da especificação
original do SQL:1999 à especificação revisada do SQL:2003. O Oracle tem suporte para a
maioria das partes da especificação, acrescentando, ao mesmo tempo, extensões proprie-
tárias. Uma extensão importante é o gatilho INSTEAD OF, disparado em lugar de um evento,
e não antes ou depois de um evento. O Oracle também tem suporte a eventos de definição de
dados e outros eventos de banco de dados. O Microsoft SQL Server fornece gatilhos de ins-
trução com acesso a dados da linha em vez de gatilhos de linha. Assim, a maioria dos SGBDs
oferece suporte ao espírito da especificação de gatilhos do SQL:2003 em termos de granu-
laridade, momento de disparo e eventos aplicáveis, mas não aderem estritamente à sintaxe de
gatilhos do SQL:2003.

11.3.2 Gatilhos do Oracle


O gatilho do Oracle contém um nome de gatilho, uma especificação de momento de disparo,
uma cláusula opcional de referência, uma granularidade opcional, uma cláusula WHEN
opcional e um bloco do PL/SQL para o corpo, explicados na lista a seguir:
• A especificação do momento de disparo envolve as palavras-chave BEFORE, AFTER ou
INSTEAD OF juntamente com um evento de gatilho utilizando as palavras-chave IN-
SERT, UPDATE ou DELETE. Com o evento UPDATE, é possível especificar uma lista
opcional de colunas. Para especificar múltiplos eventos, pode-se utilizar a palavra-chave
OR. O Oracle também tem suporte à definição de dados e outros eventos de banco de
dados, mas esses eventos não estão no escopo deste capítulo.
• A cláusula de referência permite nomes alternativos para dados novos e antigos que
podem ser referenciados em um gatilho.
• A granularidade é especificada pelas palavras-chaves FOR EACH ROW. Se essas
palavras-chaves forem omitidas, o gatilho é de instrução.
• A cláusula WHEN impõe uma restrição quando um gatilho é disparado ou executado.
Como o Oracle tem inúmeras restrições contra condições nas cláusulas WHEN, a
cláusula WHEN não é utilizada com freqüência.
• O corpo de um gatilho parece outro bloco do PL/SQL, exceto que os gatilhos têm mais
restrições nas instruções em um bloco.
404 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

Estrutura de Gatilho do Oracle:


CREATE [OR REPLACE] TRIGGER NomeGatilho
MomentoDisparoGatilho EventoGatilho
[ Cláusula de referência ]
[ FOR EACH ROW ]
[ WHEN ( Condição ) ]
[ DECLARE seqüência de instruções declarativas ]
BEGIN seqüência de instruções
[ EXCEPTION instruções para tratar exceções ]
END;

Gatilhos Introdutórios e Código de Teste


Começando com alguns gatilhos simples do Oracle, os exemplos 11.22 a 11.24 contêm gati-
lhos que são disparados respectivamente em toda instrução INSERT, UPDATE e DELETE na
tabela Curso. O Exemplo 11.25 mostra um gatilho com um evento combinado, disparado
para toda ação na tabela Curso. Os gatilhos dos exemplos 11.22 a 11.25 não têm propósito,
exceto o de descrever uma ampla variedade de sintaxe de gatilhos, assim como as explicações
apresentadas na lista a seguir.
• Um esquema comum de nomeação de gatilhos identifica o nome da tabela, as ações de
gatilhos (I para INSERT, U para UPDATE e D para DELETE) e o momento de disparo
(B para BEFORE e A para AFTER). Por exemplo, a última parte do nome do gatilho
(DIUA) do Exemplo 11.25 denota os eventos DELETE, INSERT e UPDATE juntamente
com o momento de disparo AFTER.
• No Exemplo 11.25, a palavra-chave OR na especificação do evento de gatilho permite
eventos compostos envolvendo mais de um evento.
• Não há cláusula de referência, já que são utilizados nos corpos do gatilho nomes-padrão
para a linha antiga (:OLD) e nova (:NEW).

EXEMPLO 11.22 Gatilho Disparado para Instrução INSERT na Tabela Curso Juntamente com o
Código de Teste para Disparar o Gatilho
CREATE OR REPLACE TRIGGER tr_Curso_IA
AFTER INSERT
ON Curso
FOR EACH ROW
BEGIN
-- Nenhuma referência à linha OLD porque existe somente NEW para INSERT
dbms_output.put_line('Linha Inserida');
dbms_output.put_line('NumCurso: ' || :NEW.NumCurso);
dbms_output.put_line('Descrição Curso: ' || :NEW.DescrCurso);
dbms_output.put_line('Carga Horária Curso: ' || To_Char(:NEW.CargaHora-
Curso));
END;
/
-- Instruções de teste
SET SERVEROUTPUT ON;
INSERT INTO Curso (NumCurso, DescrCurso, CargaHoraCurso)
VALUES ('SI485','Gerenciamento de Banco de Dados Avançado',4);
ROLLBACK;
Capítulo 11 Procedimentos Armazenados e Gatilhos 405

EXEMPLO 11.23 Gatilho Disparado para Toda Instrução UPDATE na Tabela Curso Juntamente
com o Código de Teste para Disparar o Gatilho
CREATE OR REPLACE TRIGGER tr_Curso_UA
AFTER UPDATE
ON Curso
FOR EACH ROW
BEGIN
dbms_output.put_line('Novos Valores de Linha');
dbms_output.put_line('NumCurso: ' || :NEW.NumCurso);
dbms_output.put_line('Descrição Curso:' || :NEW.DescrCurso);
dbms_output.put_line('Carga Horária Curso: ' || To_Char(:NEW.CargaHora-
Curso));
dbms_output.put_line('Valores Antigos de Linha');
dbms_output.put_line('NumCurso: ' || :OLD.NumCurso);
dbms_output.put_line('Descrição do Curso: ' || :OLD.DescrCurso);
dbms_output.put_line('Carga Horária do Curso: ' || To_Char(:OLD.CargaHora-
Curso));
END;
/
-- Instruções de teste
SET SERVEROUTPUT ON;
-- Adiciona linha de forma que possa ser atualizada
INSERT INTO Curso (NumCurso, DescrCurso, CargaHoraCurso)
VALUES ('SI485','Gerenciamento de Banco de Dados Avançado',4);
UPDATE Curso
SET CargaHoraCurso = 3
WHERE NumCurso = 'SI485';
ROLLBACK;

EXEMPLO 11.24 Gatilho Disparado para Toda Instrução DELETE na Tabela Curso Juntamente com
o Código de Teste para Disparar o Gatilho
CREATE OR REPLACE TRIGGER tr_Curso_DA
AFTER DELETE
ON Curso
FOR EACH ROW
BEGIN
-- Nenhuma referência à linha NEW porque existe somente OLD para DELETE
dbms_output.put_line('Linha Excluída');
dbms_output.put_line('NumCurso: ' || :OLD.NumCurso);
dbms_output.put_line('Descrição do Curso: ' || :OLD.DescrCurso);
dbms_output.put_line('Carga Horária do Curso: ' || To_Char(:OLD.Carga-
HoraCurso));
END;
/
-- Instruções de teste
SET SERVEROUTPUT ON;
-- Insere linha de forma que possa ser excluída
INSERT INTO Curso (NumCurso, DescrCurso, CargaHoraCurso)
VALUES ('SI485','Gerenciamento de Banco de Dados Avançado',4);
406 Parte Cinco Desenvolvimento de Aplicação com Bancos de Dados Relacionais

DELETE FROM Curso


WHERE NumCurso = 'SI485';
ROLLBACK;

EXEMPLO 11.25 Gatilho com Evento Combinado, Disparado para Toda Ação na Tabela Curso
Juntamente com o Código de Teste para Disparar o Gatilho
CREATE OR REPLACE TRIGGER tr_Curso_DIUA
AFTER INSERT OR UPDATE OR DELETE
ON Curso
FOR EACH ROW
BEGIN
dbms_output.put_line('Tabela Incluída');
dbms_output.put_line('NumCurso: ' || :NEW.NumCurso);
dbms_output.put_line('Descrição do Curso: ' || :NEW.DescrCurso);
dbms_output.put_line('Carga Horária do Curso: ' || To_Char(:NEW.Carga-
HoraCurso));
dbms_output.put_line('Tabela Excluída');
dbms_output.put_line('NumCurso: ' || :OLD.NumCurso);
dbms_output.put_line('Descrição do Curso: ' || :OLD.DescrCurso);
dbms_output.put_line('Carga Horária do Curso: ' || To_Char(:OLD.Carga-
HoraCurso));
END;
/
-- Instruções de teste
SET SERVEROUTPUT ON;
INSERT INTO Curso (NumCurso, DescrCurso, CargaHoraCurso)
VALUES ('SI485','Gerenciamento de Banco de Dados Avançado',4);
UPDATE Curso
SET CargaHoraCurso = 3
WHERE NumCurso = 'SI485';
DELETE FROM Curso
WHERE NumCurso = 'SI485';
ROLLBACK;

Os gatilhos, diferentemente dos procedimentos, não podem ser testados diretamente. Em vez
disso, utilizam instruções do SQL que fazem o gatilho disparar. Quando o gatilho do Exem-
plo 11.25 é disparado para uma instrução INSERT, os antigos valores são nulos. Do mesmo
modo, quando o gatilho é disparado para uma instrução DELETE, os novos valores são nulos.
Quando o gatilho é disparado para uma instrução UPDATE, os valores antigos e novos não
são nulos a menos que a tabela tenha valores nulos antes da atualização.

Gatilho BEFORE ROW para Verificação de Restrição


Os gatilhos BEFORE ROW geralmente são utilizados para restrições de integridade com-
plexas porque os gatilhos BEFORE ROW não devem conter instruções de manipulação
em SQL. Por exemplo, a matrícula em um oferecimento envolve restrição de integridade
complexa para garantir a existência de uma vaga em um oferecimento relacionado. O Exem-
plo 11.26 mostra um gatilho BEFORE ROW para garantir a manutenção da vaga quando o
aluno se matricula em um oferecimento. O gatilho garante uma quantidade de alunos matriculados
Encerra aqui o trecho do livro disponibilizado para
esta Unidade de Aprendizagem. Na Biblioteca Virtual
da Instituição, você encontra a obra na íntegra.
Dica do professor
O vídeo a seguir oferece uma apresentação das rotinas de bancos de dados, que permitem a
automatização de ações diretamente no banco de dados, reduzindo a responsabilidade das
aplicações.

Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.
Exercícios

1) O que é procedure?

A) É um conjunto de comandos SQL identificado por um nome e armazenados no banco de


dados.

B) É um comando SQL para consultar informações no banco de dados.

C) É um comando que permite criar novos registros no banco de dados.

D) É um conjunto de resultados que são obtidos em uma consulta.

E) É um roteiro de comandos que não é identificado e fica armazenado na aplicação.

2) Qual a vantagem em trabalhar com rotinas e scripts?

A) Aumentar o processamento do servidor.

B) Diminuir a intervenção do programador nas tarefas.

C) Diminuir tempo de acesso.

D) Dispensar a presença do usuário nas consultas.

E) Não existe vantagem em trabalhar com rotinas.

3) O que é um trigger?

A) É um comando do SQL para excluir tabelas e databases.

B) É um conjunto de comandos que são executados antes de uma ação ocorrer.

C) É um tipo de SGBD muito utilizado atualmente em grandes corporações.

D) É um modelo de programação, pouco relacionado com banco de dados.

E) É um conjunto de comandos que são executados após uma ação ocorrer.


4) Qual as função dos LOGS nas rotinas?

A) Saber quem é o usuário que faz a consulta.

B) Garantir a indexação dos dados.

C) Validação dos registros.

D) Registrar data e hora das execuções ou erros.

E) Garantir a integridade dos valores.

5) É uma característica da execução de rotinas automatizadas:

A) Dependem de apenas um tipo de tecnologia.

B) Possuem total autonomia na execução.

C) Requerem a intervenção humana.

D) São somente para extração de dados.

E) Não servem para Data Warehouses.


Na prática
O Comitê Gestor da Internet no Brasil tem como missão coletar, organizar e disseminar
informações, indicadores e estatísticas sobre o uso das tecnologias de informação e comunicação
no Brasil.

Veja como a rotina de programação está inserida nessa missão do CGI:


Aponte a câmera para o
código e acesse o link do
conteúdo ou clique no
código para acessar.
Saiba +
Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do professor:

PD - Criar Trigger Básica após Inserir no SQL Server 2012

Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.

Oracle PL/SQL - Procedures / Functions / Cursores

Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.

Primeira função no PostgreSQL

Aponte a câmera para o código e acesse o link do conteúdo ou clique no código para acessar.

Você também pode gostar