Você está na página 1de 52

FATEC/SO - Faculdade de Tecnologia de Sorocaba

Departamento de Processamento de Dados


Curso de Tecnologia em Processamento de Dados
G.E.P. Bancos de Dados e Redes de Computadores

Programação de SGBD

Apostila elaborada pelo Professor Antonio Cesar de Barros Munari


Colaboração: Prof.ª Maria Angélica Calixto de Andrade Cardieri
Cláudia Eiko Komeno

MAI/1998 Rev: SET/2002


Programação de SGBD

I Conceitos básicos

Introdução

Um número muito grande de operações pode ser executado sobre um banco de dados através de
comandos SQL. Algumas dessas operações podem ser bastante complexas, envolvendo um grande número
de tabelas e consultas, com aninhamento de comandos inclusive. Esses recursos da SQL não são
suficientes, entretanto, para solucionar muitos problemas comerciais típicos, uma vez que a SQL não oferece
recursos de programação estruturada, como loopings e desvios, por exemplo. Por esse motivo, os principais
fornecedores de SGBDs criaram suas próprias extensões procedurais à SQL (como a PL/SQL no Oracle ou a
Transact SQL no SQL Server), de maneira a prover recursos de programação em seus gerenciadores. Essas
extensões são verdadeiras linguagens de programação embutidas no SGBD, porém são proprietárias, ou
seja, variam de um produto para outro, apesar de implementarem alguns conceitos básicos comuns.

Assim, temos a implementação da SQL nos diversos gerenciadores de banco de dados ocorrendo
basicamente em 3 níveis, conforme a figura a seguir:

Recursos SQL do SGBD

Extensões não Extensões


SQL padrão procedurais à procedurais à
SQL SQL

No primeiro nível, chamado de SQL padrão na figura, há uma uniformidade na sintaxe de certos
recursos SQL, através de padronizações internacionais controladas por órgãos especiais como ANSI, ISO,
X-Open, etc. Entram nesta categoria uma grande parte dos comandos DML SQL, de maneira que a sintaxe e
o funcionamento de um SELECT por exemplo, tanto no Oracle como no SQL Server ou MS-Access são
iguais para a maioria das consultas básicas.

Para prover capacidades mais amplas em seus produtos, os fornecedores muitas vezes acrescentam
recursos adicionais ao mecanismo básico, como por exemplo funções de agregação extras (para cálculo de
desvio padrão ou variância, por exemplo), manipulação de strings, tipos de dados, tamanho de registro,
cláusulas extras em comandos, etc. Esses recursos variam muito de um SGBD para outro, o que significa que
soluções que os utilizem podem não funcionar em outros ambientes de banco de dados e são representados
na figura como Extensões não procedurais à SQL.

O terceiro nível, indicado como Extensões procedurais à SQL representa a linguagem de


programação procedural suportada pelo SGBD, e constitui o tema desta apostila.

Com os recursos fornecidos pelas extensões procedurais dos gerenciadores de banco de dados é
possível construir rotinas de diversos tipos que funcionam dentro do SGBD, implementando operações de
lógica mais complexa e capacidades de processamento linha a linha. Neste material utilizaremos como

1
Programação de SGBD

referência o Oracle 7.3 Server e sua linguagem de programação, chamada PL/SQL (Procedural Language /
SQL).

Tipos de sub-rotinas de banco de dados

Uma subrotina é um módulo de programa executado para uma determinada finalidade e geralmente
possui um nome, pode receber parâmetros e, conforme o caso, devolver valores. Todas as linguagens de
programação atuais (estruturadas ou orientadas a objetos) suportam esse conceito. As subrotinas de que
tratamos aqui possuem, entretanto, uma característica particular: elas são processadas pelo gerenciador de
banco de dados, de maneira que se comportam como se fossem parte dele. Em um ambiente de banco de
dados cliente / servidor, seria algo como o mostrado na figura a seguir:

Usuário Aplicação final SGBD

Subrotina

Banco de Dados

Cliente Servidor

Observe que a subrotina é executada pelo SGBD, na máquina servidora, a pedido da estação cliente.

2
Programação de SGBD

Existem 3 tipos básicos de subrotinas de banco de dados: os procedimentos, as funções e os


gatilhos.

Procedimentos

Também chamados de procedimentos armazenados (stored procedures), representam porções de


código SQL e não SQL que ficam armazenados de forma compilada no catálogo do SGBD e são ativados
explicitamente por aplicações, triggers ou outras rotinas. Como nas linguagens tradicionais de
programação, um procedimento realiza um processamento qualquer e não devolve valor algum em seu final.

Exemplo:

Ativação Procedimento

EXEC CrDep(‘D8’,‘COMPRAS’) CREATE PROCEDURE CrDep( p1 IN VARCHAR2, p2 IN VARCHAR2


) AS
BEGIN
INSERT INTO Depto
VALUES ( p1, p2 ) ;
END ;

Neste material utilizaremos o termo Procedure para referenciar os procedimentos armazenados.

Funções

A exemplo das procedures, as funções contêm porções de código SQL e não SQL; ficam
armazenadas de forma compilada no catálogo do SGBD e são ativadas explicitamente por aplicações,
triggers ou outras rotinas. A diferença básica entre o procedimento e a função é que uma função, ao realizar
um processamento, produz um valor que é devolvido (retornado) em seu final.

Exemplo:

Ativação Função

INSERT INTO teste CREATE FUNCTION CalcDobro ( p1 IN NUMBER ) RETURN


VALUES (1, NUMBER IS
CalcDobro(1));
p2 NUMBER ;
Ou BEGIN
p2 := p1 * 2 ;
SELECT NmFunc, RETURN p2 ;
CalcDobro( END ;
VrSalario )
FROM Funcionario ;

3
Programação de SGBD

Gatilhos

Um gatilho (ou trigger) é uma porção de código composto por instruções SQL e não-SQL que ficam
armazenados no catálogo do SGBD e que são ativados automaticamente pelo gerenciador de banco de
dados quando um determinado evento ocorre. Em princípio um gatilho poderia ser entendido como uma
procedure ativada implicitamente pelo SGBD.

Exemplo:

Ativação Gatilho

DELETE FROM Funcionario CREATE TRIGGER ApgFunc


WHERE NrMatric = ‘1029’ ; BEFORE DELETE ON Funcionario
FOR EACH ROW
BEGIN
INSERT INTO LogFunc (DtOp, Usuario, Chave )
VALUES ( SYSDATE, USER, :old.NrMatric ) ;
END ;

Neste material utilizaremos o termo Trigger para referenciar os gatilhos de banco de dados.

Informações sobre subrotinas no catálogo do Oracle

1) Para consultar os nomes das subrotinas já criadas no SGBD:

SELECT object_name FROM user_objects WHERE object_type = ‘PROCEDURE’ ;


ou
SELECT object_name FROM user_objects WHERE object_type = ‘FUNCTION’ ;
ou
SELECT object_name FROM user_objects WHERE object_type = ‘TRIGGER’ ;

2) Para visualizar todos os objetos criados no banco de dados:

SET LINESIZE 255


SELECT object_type, object_name FROM user_objects ORDER BY 1 ;

3) Para visualizar o código fonte referente a uma subrotina já criada no SGBD:

SELECT text FROM user_source WHERE name = <nome_rotina> ;

Ex: SELECT text FROM user_source WHERE name = ‘APGFUNC’ ;

4
Programação de SGBD

II PL/SQL: Estrutura Básica

Composição dos módulos PL / SQL

Observe a rotina criada a seguir.

001 CREATE OR REPLACE PROCEDURE AtuSalario (vDepto IN CHAR, vPerc IN NUMBER)


AS
002
003 vData DATE ;
004 cCdOp CONSTANT CHAR( 1 ) := ‘U’ ; -- Codigo p/ “Update”
005
006 BEGIN
007 vData := SYSDATE ;
008
009 UPDATE Funcionario
010 SET VrSalario = VrSalario * vPerc, DtUltAlt = vData
011 WHERE CdDepto = vDepto ;
012
013 INSERT INTO LogFunc ( DtOp, Usuario, Chave, CdOp )
014 VALUES ( vData, USER, ‘VRSALARIO’, cCdOp ) ;
015
016 COMMIT ;
017
018 -- Tratamento de excecoes
019 EXCEPTION
020 WHEN NO_DATA_FOUND THEN
021 raise_application_error ( -20001, ‘Depto nao cadastrado’ ) ;
022 WHEN VALUE_ERROR THEN
023 raise_application_error ( -20002, ‘Salario invalido’ ) ;
024 WHEN OTHERS THEN
025 raise_application_error ( -20099, ‘Erro geral’ ) ;
026 END ;

Esse exemplo representa um módulo PL / SQL típico (uma procedure, no caso), e permite visualizar 4
regiões básicas, conforme o esquema:

Assinatura da rotina (opcional) Linha 001

Área de declarações (opcional) Linhas 003 e 004

Área de instruções (obrigatória) Linhas 006 a 017

Área de exceções (opcional) Linhas 019 a 025

A primeira área é da a Assinatura da rotina, que contém a declaração do nome e do tipo da


subrotina, ou seja, se é uma procedure, função ou trigger, por exemplo. Também indica a lista de parâmetros
que o módulo recebe (se houver algum) e, caso seja uma função, indica também a variável que conterá o seu

5
Programação de SGBD

valor de retorno. A rigor, a assinatura seria apenas esse conjunto de informações, mas na linha inicial
aparece também o comando CREATE necessário para gerar a rotina no SGBD. Esta área é opcional, pois
podemos ter rotinas sem nome ( blocos anônimos) executadas apenas uma vez via linha de comando.
A segunda parte é a Área de declarações gerais, onde são definidas as variáveis, constantes,
cursores e exceções utilizadas pelo módulo. É opcional e, conforme o tipo da rotina, pode requerer seu início
com a palavra reservada DECLARE, como nos casos dos blocos anônimos e triggers, por exemplo.

Em seguida, temos a única área obrigatória de um módulo, a Área de instruções (ou comandos). É
nela que colocamos a lógica da rotina. Inicia-se com uma declaração BEGIN.

Finalmente, temos uma área opcional chamada Área de exceções, onde são colocadas instruções para
o tratamento de erros na rotina, de acordo com um mecanismo interno disponível no SGBD e que pode
sofrer também algumas adaptações por parte do programador.

Toda rotina termina com um END, de maneira a fechar o BEGIN inicial da área de instruções.
Comentários iniciam-se com os caracteres “--” (dois hifens seguidos) e prosseguem até o fim da linha atual,
conforme pode ser observado nas linhas 4 e 18 do exemplo anterior.

Variáveis, constantes e tipos de dados

O conceito de variável e de constante do PL / SQL é o mesmo das outras linguagens estruturadas:


são regiões de memória que possuem um nome definido pelo programador e podem conter valores de um
determinado tipo de dado. O conteúdo de uma variável pode se modificar durante a execução da rotina,
enquanto que o valor da constante permanece fixo. Os tipos de dados permitidos são os seguintes:

Tipo do Dado Subtipos Descrição


NUMBER DEC, DECIMAL, DOUBLE, PRECISION, Permite criar variáveis ou constantes que
FLOAT, INT, INTEGER, NUMERIC, armazenem números fixos ou de ponto
REAL, SMALLINT flutuante com precisão de até 38 dígitos.
BINARY_INTEGER NATURAL, POSITIVE Indicado para a criação de variáveis ou
constantes que armazenem números
inteiros com sinal, de forma a acelerar
algumas operações com inteiros no
Oracle. A faixa de valores válidos vai de -
231-1 até 231-1.
CHAR CHARACTER, STRING Permite texto de tamanho fixo até 32Kb.
VARCHAR2 VARCHAR Permite texto de tamanho variável de até
32Kb.
DATE Utilizado para armazenar datas, horas,
minutos e segundos.
BOOLEAN Indicado para valores lógicos TRUE e
FALSE.
RECORD Utilizado para construir tipos complexos
estruturados, como imagens de registros
de tabelas, por exemplo.
TABLE Utilizado para conter conjuntos de dados
com várias ocorrências.

É possível também utilizar os atributos especiais %TYPE e %ROWTYPE para fazer com que uma
variável ou constante herde o tipo de dado de um campo ou registro de tabela. Observe os exemplos a
seguir.

6
Programação de SGBD

7
Programação de SGBD

Exemplo 1:
DECLARE
NmUser VARCHAR2( 30 ) ;
TxUnica CONSTANT INTEGER := 2 ;
DtSist DATE := SYSDATE ;
QtFunc NUMBER( 5, 0 ) ;
VrSalario Funcionario.VrSalario%TYPE ;
QtCaixas QtFunc%TYPE ;

Exemplo 2:
DECLARE
TYPE RegCargo IS RECORD
( CdCargo CHAR( 2 ) NOT NULL := 0 ,
NmCargo CHAR( 30 ));

vCargo RegCargo ;

Exemplo 3:
DECLARE
RegFunc Funcionario%ROWTYPE ;

Exemplo 4:
DECLARE
TYPE TabNMatr IS TABLE OF CHAR(4) INDEX BY BINARY_INTEGER;
vNrMatr TabNMatr ;

8
Programação de SGBD

Operadores

A tabela abaixo apresenta o conjunto de operadores do PL / SQL.

Operador Significado
+ Adição
- Subtração
* Multiplicação
/ Divisão
** Exponenciação
= Igual a
> Maior que
< Menor que
>= Maior ou igual a
<= Menor ou igual a
<> Diferente de
^= Diferente de
!= Diferente de
|| Concatenação de caracteres
:= Atribuição
-- Início de comentário (até fim da linha)
/* Início de comentário
*/ Final de comentário

Variáveis especiais

Em um ambiente de programação Oracle existem disponíveis para o programador algumas variáveis


especiais bastante interessantes para determinadas aplicações.

Variável Tipo de Dado Conteúdo


USER Caracter Nome do usuário corrente
SYSDATE Date Data e hora atuais do sistema
UID Caracter / Number Número de identificação do usuário corrente

Quando for necessário obter o valor de uma dessas variáveis a partir de um comando SELECT que
não referencia diretamente nenhuma tabela ou view normal do banco de dados, deve-se indicar a tabela
especial DUAL na cláusula FROM do comando, conforme o exemplo a seguir.

SELECT SYSDATE, USER FROM Dual ;

9
Programação de SGBD

Estruturas de controle de fluxo de programa

São as estruturas básicas que permitem compor uma lógica procedural em uma rotina, como os
desvios e repetições.

IF .. END IF

É utilizado para permitir que o fluxo do programa seja desviado para um determinado ponto da lógica,
conforme o resultado de uma comparação feita. Existem 3 variações desta estrutura, cobrindo desde o caso
mais simples até possibilidades de desvio múltiplo.

Variação 1: Desvio simples, sem cláusula ELSE

IF <expL> THEN IF var1 > 10 THEN


<operação1> ; var2 := var1 + 1 ;
... var3 := var1 / 4 ;
<operaçãoN> ; END IF ;
END IF ;

Variação 2: Desvio simples, com cláusula ELSE

IF <expL> THEN IF var1 > 10 THEN


<operação1> ; var2 := var1 + 1 ;
... var3 := var1 / 4 ;
<operaçãoN> ; ELSE
ELSE var2 := 0 ;
<operação1> ; var3 := var1 / 2 ;
... END IF ;
<operaçãoN> ;
END IF ;

Variação 3: Desvio múltiplo

IF <expL1> THEN IF vNota > 9 THEN


<operação1> ; vConceito := ‘E’ ;
... ELSIF vNota > 8 THEN
<operaçãoN> ; vConceito := ‘A’ ;
ELSIF <expL2> THEN ELSIF vNota > 7 THEN
... vConceito := ‘B’ ;
ELSIF <expLN> THEN ELSE
... vConceito := ‘C’ ;
[ ELSE END IF ;
<operação1> ;
...
<operaçãoN> ; ]
END IF ;

10
Programação de SGBD

LOOP ... EXIT [WHEN ...] ... END LOOP

É uma estrutura de repetição simples, cuja saída é indicada pela instrução EXIT. É possível testar a
condição de saída através de uma estrutura IF / END IF conforme o exemplo abaixo ou, como é mais
recomendável, através da cláusula opcional WHEN na própria instrução EXIT, conforme mostra o segundo
exemplo.

Sintaxe básica: Exemplo 1:

LOOP ...
<operação1> ; LOOP
... ...
<operaçãoN> ; vCont := vCont + 1 ;
EXIT [ WHEN <expL> ] ; IF vCont > 10 THEN
<operaçãoN + 1> ; EXIT ;
... END IF ;
END LOOP ; ...
END LOOP ;
...

Exemplo 2:

...
LOOP
...
vCont := vCont + 1 ;
EXIT WHEN vCont > 10 ;
...
END LOOP ;
...

WHILE ... LOOP ... END LOOP

O ciclo de iterações é executado enquanto a condição da cláusula WHILE for verdadeira.

Sintaxe básica: Exemplo:

WHILE <expL> LOOP ...


<operação1> ; vCont := 1 ;
... WHILE vCont <= 10 LOOP
<operaçãoN> ; ...
END LOOP ; vCont := vCont + 1 ;
...
END LOOP ;
...

11
Programação de SGBD

12
Programação de SGBD

FOR... IN ... LOOP ... END LOOP

O ciclo de iterações é executado um número predefinido de vezes, controlado por meio de um


contador.

Sintaxe básica: Exemplo1:

FOR <variável> IN [REVERSE] <faixa> LOOP ...


<operação1> ; FOR vCont IN 1 .. 10 LOOP
... ...
<operaçãoN> ; ...
END LOOP ; END LOOP ;
...

Exemplo 2:

...
FOR vCont IN REVERSE 1..5 LOOP
...
...
END LOOP ;
...

Observações

A variável utilizada para o controle do looping FOR pode ser criada automaticamente no início da
estrutura de repetição e, nesse caso, não deve ser declarada explícitamente na área de declarações da rotina,
podendo ser utilizada então apenas dentro do looping. O incremento do valor dessa variável (ou o
decremento, se a cláusula REVERSE for utilizada) é automático. Operações de atribuição não podem ser
feitas para essa variável.

13
Programação de SGBD

III PL/SQL construções principais

Cursores

Uma necessidade básica da maioria das aplicações de banco de dados é obter dados a partir das tabelas ou
visões e colocá-las nas variáveis das rotinas. Na abordagem dos SGBDs isso é feito através de um recurso
chamado “Cursor”, cujo conceito é mais ou menos padronizado nos vários produtos dos diversos
fornecedores. A idéia básica da utilização de cursores é direcionar os dados obtidos a partir de uma
instrução SELECT para um conjunto de variáveis criado especificamente para esse fim. Existem, em
princípio, dois tipos de cursores, os implícitos e os explícitos, que serão vistos em maiores detalhes a
seguir.

Cursores implícitos

Um cursor deste tipo é implementado através da colocação da cláusula INTO no SELECT, conforme
pode ser visto no exemplo a seguir.

001 CREATE OR REPLACE FUNCTION AchaFunc ( pNrMatric IN CHAR ) RETURN CHAR


IS
002
003 vResult CHAR( 4 ) ;
004
005 BEGIN
006 SELECT Funcionario.NrMatric
007 INTO vResult
008 FROM Funcionario
009 WHERE Funcionario.NrMatric = pNrMatric ;
010 RETURN vResult ;
011
012 EXCEPTION
013 WHEN NO_DATA_FOUND THEN
014 RETURN ‘9999’ ;
015 END ;

Observe na linha 007 a presença da cláusula INTO, indicando que o valor retornado pelo comando
será colocado na variável vResult, declarada na rotina. Caso houvesse mais de um valor a ser colocado em
variáveis, a cláusula INTO deveria conter uma lista com os nomes de todas as variáveis, separadas por
vírgula. Essas variáveis deveriam aparecer em uma ordem compatível com os campos projetados pelo
SELECT.

Dois cuidados básicos devem ser tomados ao utilizar-se cursores implícitos:

a) as variáveis que receberão os dados obtidos pelo SELECT deverão ser declaradas com tipo igual
ao do campo correspondente na tabela, o que torna bastante indicado nesses casos utilizar o
atributo %TYPE ao declarar a variável, para evitar problemas de incompatibilidade;
b) o comando deve retornar no máximo uma única linha, senão uma exceção TOO_MANY_ROWS
será gerada.

14
Programação de SGBD

Não há uma declaração formal do cursor, apenas das variáveis a serem atualizadas pelo comando.

Cursores explícitos

Os cursores explícitos são chamados dessa forma porque são declarados formalmente na Área de
declarações do módulo, ao contrário do que ocorre com os cursores implícitos. São tipicamente mais
flexíveis e poderosos que os cursores implícitos, podendo substituí-los em qualquer situação.

Para sua utilização são necessários alguns passos básicos:


• declarar o cursor ;
• declarar as variáveis que receberão os dados ;
• abrir (uma espécie de preparação) o cursor na área de instruções ;
• ler os dados produzidos pelo cursor ;
• fechar (desalocar a memória) do cursor.

O exemplo a seguir ilustra a utilização de um cursor explícito equivalente àquele utilizado para
demonstrar o cursor implícito. Analise as principais diferenças entre essa solução e a anterior.

001 CREATE OR REPLACE FUNCTION AchaFunc ( pNrMatric IN CHAR ) RETURN CHAR


IS
002
003 vResult CHAR( 4 ) ;
004 CURSOR vCursor IS
005 SELECT Funcionario.NrMatric
006 FROM Funcionario
007 WHERE Funcionario.NrMatric = pNrMatric ;
008
009 BEGIN
010 OPEN vCursor ;
011 FETCH vCursor INTO vResult ;
012 IF vCursor%FOUND THEN
013 RETURN vResult ;
014 ELSE
015 RETURN ‘9999’ ;
016 END IF ;
017 CLOSE vCursor ;
018 END ;

Inicialmente, existe a declaração do cursor nas linhas 004 a 007, onde não aparece a cláusula INTO. O
nome dado ao cursor segue as mesmas regras para os nomes de variáveis. A sintaxe básica dessa
declaração é a seguinte:

CURSOR <nome do cursor> IS <declaração SELECT> ;

Ao fazermos a declaração, apenas foi definida uma estrutura que será utilizada posteriormente,
quando o SELECT for executado e a recuperação das linhas for realizada. A primeira instrução realmente
executável relativa ao cursor é a sua abertura, feita na área de instruções através do comando OPEN. É no
momento da abertura que o SELECT é executado e as linhas recuperadas tornam-se disponíveis para uso. A
sintaxe do comando OPEN é:

OPEN <nome-do-cursor>;

Para obtermos as linhas que foram recuperadas pela consulta, devemos buscá-las, uma a uma, na
estrutura do cursor e copiar seus dados para as variáveis correspondentes. Isso é obtido através do
comando FETCH:

15
Programação de SGBD

FETCH <nome-do-cursor> INTO <lista de variáveis> ;

Ao final do processamento, o cursor é fechado através de um comando CLOSE, cuja sintaxe é:

CLOSE <nome-do-cursor> ;

Além dos recursos básicos ligados aos cursores, existem três outros que merecem atenção: a
possibilidade da passagem de parâmetros para os cursores; os chamados
atributos de cursor e cursores explícitos via looping FOR.

Parâmetros de cursor

Geralmente o comando SELECT de um cursor possui uma cláusula WHERE que especifica uma
seleção de linhas a serem retornadas. Muitas vezes, temos necessidade de variar um dado a ser comparado
nessa cláusula, e isso pode ser feito através de uma espécie de parâmetro passado para o cursor no
momento de sua abertura. Observe o exemplo a seguir:

001 DECLARE
002 vMatr CHAR( 4 ) ;
003 vNome VARCHAR2( 30 ) ;
004 CURSOR LstFunc ( pSexo CHAR ) IS
005 SELECT NrMatric, NmFunc
006 FROM Funcionario
007 WHERE Sexo = pSexo ;
008
009 BEGIN
010 OPEN LstFunc( ‘F’ ) ;
011 FETCH LstFunc INTO vMatr, vNome ;
012 CLOSE LstFunc ;
013
014 OPEN LstFunc( ‘M’ ) ;
015 FETCH LstFunc INTO vMatr, vNome ;
016 CLOSE LstFunc ;
017
018 END ;

Ao abrirmos pela primeira vez, na linha 10, o cursor assume ‘F’ como sendo o conteúdo da variável
pSexo, e assim, recupera apenas os funcionários do sexo feminino. Na segunda vez que é aberto, o
parâmetro passado é ‘M’, e apenas os funcionários de sexo masculino são processados.

Atributos de cursor

Durante a utilização de um cursor em uma rotina, uma série de valores podem ser testados, de maneira
a permitir a monitoração do estado corrente do processamento. Esses valores são obtidos através de
variáveis especiais mantidas pelo sistema, chamadas de Atributos do cursor. Todos eles têm seu nome
começando com o símbolo “%” (sinal de porcentagem) e são referenciados colocando-se o nome do cursor
imediatamente antes do “%”. A seguir um pequeno resumo com esses atributos e suas características
principais.

16
Programação de SGBD

Atributo Tipo de Dado Descrição


%ISOPEN BOOLEAN Indica se o cursor referenciado está aberto (TRUE) ou fechado (FALSE).

%ROWCOUNT NUMBER É um contador que indica quantas linhas já foram recuperadas através de
um comando FETCH.

%NOTFOUND BOOLEAN Indica o resultado do último FETCH: se foi bem sucedido, seu valor é
FALSE, senão TRUE

%FOUND BOOLEAN Indica o resultado do último FETCH: se foi bem sucedido, seu valor é
TRUE, senão FALSE.

Alguns exemplos de utilização desses atributos de cursor:

Exemplo 1:

001 DECLARE
002 vMatr CHAR( 4 ) ;
003 vSalario Funcionario.VrSalario%TYPE ;
004 CURSOR TesteCur IS
005 SELECT NrMatric, VrSalario
006 FROM Funcionario ;
007
008 BEGIN
... .. .
018 IF TesteCur%ISOPEN THEN
019 CLOSE TesteCur ;
020 ELSE
021 OPEN TesteCur ;
022 END IF ;
... ...
034 END ;

Exemplo 2:

001 CREATE OR REPLACE FUNCTION AchaFunc ( pNrMatric IN CHAR ) RETURN CHAR


IS
002
003 vResult CHAR( 4 ) ;
004 CURSOR vCursor IS
005 SELECT Funcionario.NrMatric
006 FROM Funcionario
007 WHERE Funcionario.NrMatric = pNrMatric ;
008
009 BEGIN
010 OPEN vCursor ;
011 FETCH vCursor INTO vResult ;
012 IF vCursor%FOUND THEN
013 RETURN vResult ;
014 ELSE
015 RETURN ‘9999’ ;
016 END IF ;
017 CLOSE vCursor ;
018 END ;

17
Programação de SGBD

Exemplo 3:

001 DECLARE
002 vMatr CHAR( 4 ) ;
003 vSalario Funcionario.VrSalario%TYPE ;
004 vTotSal Funcionario.VrSalario%TYPE := 0 ;
005 CURSOR TesteCur IS
006 SELECT NrMatric, VrSalario
007 FROM Funcionario
008 ORDER BY VrSalario DESC ;
009
010 BEGIN
011 OPEN TesteCur ;
012 LOOP
013 FETCH TesteCur INTO vMatr, vSalario ;
014 EXIT WHEN TesteCur%NOTFOUND OR TesteCur%ROWCOUNT = 3 ;
015 vTotSal := vTotSal + vSalario ;
016 END LOOP ;
... ...
027 CLOSE TesteCur ;
028 END ;

18
Programação de SGBD

Cursores explícitos com looping FOR

Esta é uma variação do cursor explícito em que este fica embutido em uma estrutura FOR responsável
pelo seu processamento. É uma espécie de simplificação, pois nessa estrutura o cursor é aberto uma vez, as
linhas são processadas (uma a cada passagem do looping) e o cursor é fechado automaticamente no final
das iterações.

A sintaxe básica é a seguinte:

FOR <nome do registro> IN <nome do cursor> LOOP


... ;
... ;
END LOOP ;

O <nome do registro> é um nome de variável do tipo RECORD criada automaticamente na entrada do


looping. O cursor propriamente dito é declarado normalmente na área de declarações da rotina, e pode
prever a utilização de parâmetros. Os atributos de cursor vistos anteriormente estão disponíveis
normalmente neste tipo de estrutura.

Exemplo (com cursor explícito convencional):

001 DECLARE
002 vFunc Funcionario%ROWTYPE ;
003 CURSOR CurFunc IS
004 SELECT *
005 FROM Funcionario ;
006
007 BEGIN
008 OPEN CurFunc ;
009 LOOP
010 FETCH CurFunc INTO vFunc ;
011 EXIT WHEN CurFunc%NOTFOUND ;
012 IF vFunc.VrSalario > 500 THEN
... ...
018 END IF ;
... ...
016 END LOOP ;
... ...
084 CLOSE CurFunc ;
085 END ;

Exemplo (com cursor explícito em looping FOR):

001 DECLARE
002 CURSOR CurFunc IS
003 SELECT *
004 FROM Funcionario ;
005 BEGIN
006 FOR vFunc IN CurFunc LOOP
007 IF vFunc.VrSalario > 500 THEN
... ...
014 END IF ;
... ...
040 END LOOP ;
... ...
084 END ;

19
Programação de SGBD

20
Programação de SGBD

Manipulação de erros

Toda linguagem deve proporcionar aos programadores recursos para uma fácil e adequada
intervenção em casos de erro de execução, de maneira a minimizar ou evitar problemas maiores sobre os
dados. Em PL/SQL isso é obtido através de um mecanismo baseado no conceito de exceções, que podem ser
classificadas em internas ou externas. Uma exceção nada mais é do que uma notificação de erro, realizada
pelo programa, através de um conjunto de códigos previamente definidos, que podem ser detectadas na área
de exceções de um módulo. Tendo sido detectada a ocorrência, é fácil para o programador associar uma
seqüência de instruções para o tratamento daquela espécie de erro.

Ao conjunto de exceções padrão previamente definidas para o PL/SQL damos o nome de exceções
internas, que são mostradas no quadro a seguir.

Exceção Descrição
CURSOR_ALREADY_OPEN Indica uma tentativa de se abrir um cursor já aberto no programa.
DUP_VAL_ON_INDEX Tentativa de se inserir uma chave duplicada em uma coluna definida
como única.
INVALID_CURSOR Referência a cursor inválido ou tentativa de operação inválida sobre o
cursor.
INVALID_NUMBER Indica que um valor não numérico está sendo usado onde era esperado
apenas números.
LOGIN_DENIED Solicitação de login negada
NO_DATA_FOUND Não foram encontrados dados para atender a uma requisição feita
através de SELECT INTO.
NOT_LOGGED_ON Usuário não está conectado no Oracle no momento (está off-line).
PROGRAM_ERROR Erro interno do PL/SQL
STORAGE_ERROR Erro de memória do PL/SQL
TIMEOUT_ON_RESOURCE Ocorreu o atingimento do tempo designado para o usuário na sua conta.
TOO_MANY_ROWS A instrução SELECT ... INTO encontrou mais de uma linha.
TRANSACTION_BACKED_OUT Um servidor remoto desfez a transação
VALUE_ERROR Erro de aritmética, de conversão, truncagem ou de restrição.
ZERO_DIVIDE Tentativa de se dividir um número por zero.

A detecção e o tratamento da condição de erro é feita conforme mostrado na rotina criada a seguir.

21
Programação de SGBD

001 CREATE OR REPLACE PROCEDURE AtuSalario (vDepto IN CHAR, vPerc IN NUMBER)


AS
002
003 vData DATE ;
004 cCdOp CONSTANT CHAR( 1 ) := ‘U’ ; -- Codigo p/ “Update”
005
006 BEGIN
007 vData := SYSDATE ;
008
009 UPDATE Funcionario
010 SET VrSalario = VrSalario * vPerc, DtUltAlt = vData
011 WHERE CdDepto = vDepto ;
012
013 INSERT INTO LogFunc ( DtOp, Usuario, Chave, CdOp )
014 VALUES ( vData, USER, ‘VRSALARIO’, cCdOp ) ;
015
016 COMMIT ;
017
018 -- Tratamento de excecoes
019 EXCEPTION
020 WHEN NO_DATA_FOUND THEN
021 raise_application_error ( -20001, ‘Depto nao cadastrado’ ) ;
022 WHEN VALUE_ERROR THEN
023 raise_application_error ( -20002, ‘Salario invalido’ ) ;
024 WHEN OTHERS THEN
025 raise_application_error ( -20999, ‘Erro geral’ ) ;
026 END ;

Geralmente, o procedimento adotado para o tratamento de uma exceção é o cancelamento do


processamento, com a conseqüente reversão de todas as operações feitas na transação com problemas. Isso
pode ser obtido através da função padrão RAISE_APPLICATION_ERROR, que permite ainda retornar um
código e uma mensagem de erro para a rotina chamadora do módulo em que o problema ocorreu.

22
Programação de SGBD

Triggers

Um trigger, ou gatilho, de banco de dados é uma rotina associada a uma tabela e que é disparada
automaticamente sempre que um evento específico ocorre. Rotinas deste tipo possuem uma estrutura muito
particular, que é possível esquematizar da seguinte forma:

TRIGGER <nome do trigger>


<BEFORE | AFTER> <evento de banco de dados>
[FOR EACH ROW]
[DECLARE
... ]
BEGIN
...
END ;

Um trigger é criado através da instrução CREATE TRIGGER, e fica armazenado no catálogo do SGBD,
que o ativa a cada vez que o evento de banco de dados definido para ele ocorre. Esses eventos são as
operações que provocam algum tipo de atualização nos dados de uma tabela, ou seja, instruções INSERT,
UPDATE ou DELETE. Dentro de um trigger é possível declarar e utilizar variáveis, constantes, cursores,
desvios e loopings, como em rotinas de outros tipos. Também é possível ativar outras rotinas (procedures e
functions) em seu interior.

Para uma melhor compreensão das características e recursos disponíveis com os triggers, observe os
exemplos a seguir, procurando identificar o efeito de cada um deles e as condições em que são disparados.

Exemplo 1:

Ativação Trigger

DELETE FROM Funcionario CREATE TRIGGER ApgFunc


WHERE NrMatric = ‘1029’ ; BEFORE DELETE ON Funcionario
FOR EACH ROW
BEGIN
INSERT INTO LogFunc (DtOp, Usuario, Chave )
VALUES ( SYSDATE, USER, :old.NrMatric ) ;
END ;

23
Programação de SGBD

Exemplo 2:

Ativação Trigger

UPDATE Funcionario CREATE TRIGGER AltSalFunc


SET VrSalario = VrSalario * AFTER UPDATE OF VrSalario ON Funcionario
1.1 DECLARE
WHERE CdDepto IN ( ‘D1’,‘D3’ CdOp CONSTANT CHAR := ‘S’ ;
); BEGIN
INSERT INTO LogFunc (DtOp, Usuario, TpOp )
VALUES ( SYSDATE, USER, CdOp ) ;
END ;

Exemplo 3:

Ativação Trigger

UPDATE Funcionario CREATE TRIGGER AltSalFunc


SET VrSalario = VrSalario * AFTER UPDATE OF VrSalario ON Funcionario
1.1; FOR EACH ROW
WHEN (new.VrSalario > 5000)
DECLARE
CdOp CONSTANT CHAR := ‘X’ ;
BEGIN
INSERT INTO LogFunc (DtOp, Usuario, TpOp )
VALUES ( SYSDATE, USER, CdOp ) ;
END ;

Exemplo 4:

Ativação Trigger

UPDATE Cargo CREATE TRIGGER AltNmCargo


SET NmCargo = ‘OFICIAL AFTER UPDATE OF NmCargo ON Cargo
ADMIN.’ FOR EACH ROW
WHERE CdCargo = ‘C4’ ; BEGIN
INSERT INTO LogCargo
(DtOp, Usuario, ChOrig, ChNova )
VALUES ( SYSDATE, USER, :old.NmCargo,
:new.NmCargo ) ;
END ;

24
Programação de SGBD

25
Programação de SGBD

IV Estudo de caso

Veremos agora um caso prático que nos oferecerá a oportunidade de utilizar alguns recursos muito
importantes de um gerenciador de banco de dados. Criação e alteração de tabelas, construção e utilização de
rotinas no SGBD acompanhados de uma discussão sobre algumas questões pertinentes sobre o projeto de
bancos de dados são os tópicos que serão abordados, através do acompanhamento da evolução de um
pequeno modelo de banco de dados para a manutenção de contas-corrente em um banco. O modelo é
propositalmente simplificado, de maneira a ressaltar os tópicos centrais de nosso assunto: SQL Data
Definition Language e PL/SQL.

Inicialmente, o modelo a ser implantado seria o seguinte:

@ Núm. Conta
Data Abertura
(E) Agência

Conta
Cliente Mantém Possui Movimento
Corrente

@ Cód. Cliente (E) Cliente @ Núm. Mov.


Nome (E) Conta Corr. (E) Conta Corr.
Endereço Data Movim.
Telefone Tipo Movim
Pertence
CPF Valor Movim.
RG Descr. Movim.

Agência

@ Núm. Agênc.
Nome Agência
Endereço

Observe que o modelo utiliza uma notação de modelagem de dados que combina a representação de
Peter Chen com uma forma particular de representação de cardinalidades. O projeto suporta contas
conjuntas, através do relacionamento N:N Mantém. Com isso, teremos 5 tabelas, correspondentes a cada um
dos arranjos de dados sugeridos no modelo:

26
Programação de SGBD

Entidade Tabela
Cliente Cliente
Mantém CliConta
Conta Conta
Agência Agencia
Movimento Movimento

Utilizamos um arroba (@) para representar a chave primária de cada tabela e uma letra ‘E’ para
designar cada uma das chaves estrangeiras. Todas as tabelas possuem uma chave primária simples, exceto
CliConta, que representa o relacionamento Mantém: nesse caso a chave primária é composta, formada pela
combinação dos dois atributos (Cliente e Conta Corr.) que, individualmente, representam chaves
estrangeiras.

Além dessas características, consideraremos ainda as seguintes especificações adicionais sobre os


valores dos atributos:

Entidade Atributo Restrições


Cliente Nome obrigatório, deve ser único na tabela.
Endereço obrigatório.

Agência Nome Agência obrigatório, deve ser único na tabela.


Telefone obrigatório.

Conta Data Abertura obrigatório.


Agência obrigatório.

Mantém Cliente obrigatório.


Conta Corr. obrigatório.

Movimento Conta Corr. obrigatório.


Data Movim. obrigatório.
Tipo Movim. obrigatório, deve ser “C” ou “D”.
Valor Movim. obrigatório, deve ser maior que zero.
Descr. Movim. obrigatório.

Além de observar essas restrições deveremos, para criar as tabelas, levar em conta os tipos de dados
mais adequados a cada atributo, bem como definir nomes de campos que sejam mnemônicos e guardem
alguma correspondência com os nomes dos atributos, mas que também obedeçam às regras de nomes
impostas pelo Oracle.

27
Programação de SGBD

Observe o script SQL que gera as tabelas correspondentes ao modelo no Oracle 7.3:

-- *
-- * Arquivo: CriaBan.sql
-- *
-- * Autor: Cesar
-- *
-- * Data: 20/3/97
-- *
-- * Finalidade: Criar as tabelas referentes ao Banco Simplificado
-- *

CREATE TABLE Agencia


( CdAgencia CHAR(4) PRIMARY KEY,
NmAgencia VARCHAR2(30) NOT NULL UNIQUE,
EndAgencia VARCHAR2(50),
TelAgencia VARCHAR2(15) NOT NULL ) ;

CREATE TABLE Cliente


( CdCliente CHAR(5) PRIMARY KEY,
NmCliente VARCHAR2(40) NOT NULL UNIQUE,
EndCliente VARCHAR2(50) NOT NULL,
TelCliente VARCHAR2(15),
RGCliente VARCHAR2(15),
CPFCliente VARCHAR2(15) ) ;

CREATE TABLE Conta


( NrConta NUMBER(8) PRIMARY KEY,
DtAbertura DATE NOT NULL,
CdAgencia CHAR(4) NOT NULL REFERENCES
Agencia);

CREATE TABLE CliConta


( CdCliente CHAR(5) NOT NULL REFERENCES
Cliente,
NrConta NUMBER(8) NOT NULL REFERENCES Conta,
PRIMARY KEY (CdCliente, NrConta) ) ;

CREATE TABLE Movimento


( NrMov NUMBER(12) PRIMARY KEY,
NrConta NUMBER(8) NOT NULL REFERENCES Conta,
DtMov DATE NOT NULL,
TpMov CHAR(1) NOT NULL
CHECK ( TpMov IN( 'C', 'D' )
),
VrMov NUMBER(12,2) NOT NULL CHECK ( VrMov > 0
),
DsMov VARCHAR2(20) NOT NULL ) ;

-- *
-- * Fim deste script
-- *

28
Programação de SGBD

Você deve ter reparado que existe uma ordem certa para criar as tabelas, uma vez que especificamos
as restrições de integridade referencial (chaves estrangeiras) no próprio comando que cria as tabelas. Dessa
forma, primeiro temos que criar as tabelas que são referenciadas pelas demais. Se esse cuidado não for
tomado, um comando CREATE TABLE que declare uma chave estrangeira para uma tabela ainda não
existente irá falhar.

Uma alternativa seria criar as tabelas apenas com as restrições básicas de NULL / NOT NULL. Após
criadas todas as tabelas, seriam emitidos comandos ALTER TABLE que acrescentariam a cada tabela as
respectivas restrições para implementar as outras regras de integridade (CHECK, UNIQUE, PRIMARY KEY,
FOREIGN KEY). Nesse caso, como ao especificar as chaves estrangeiras todas as tabelas já existem, não há
problema algum relacionado com a ordem das declarações.

Suponhamos que comandos INSERT tenham sido emitidos para povoar com dados cada uma das
tabelas de nosso modelo. Os dados para estudo serão os seguintes:

SQL> SELECT * FROM Agencia;

CDAG NMAGENCIA ENDAGENCIA TELAGENCIA


---- ---------------------- ------------------------------ ----------
0040 Praca da Republica Praca da Republica, 200 555-4433
0050 Centro R 15 de Novembro, 800 555-2211
0060 Prefeitura Av. Sao Paulo, 100 555-6612

3 rows selected.

SQL> SELECT CdCliente, NmCliente, EndCliente FROM Cliente;

CDCLI NMCLIENTE ENDCLIENTE


----- -------------------------------- --------------------------------
00001 ANA JUNQUEIRA R SALVADOR, 40
00002 JOAO DA SILVA AV CAMPINAS, 89
00003 MAIRA FIGUEIREDO R OLAVO BILAC, 430
00004 PEDRO SANTOS R RUI BARBOSA, 230
00005 SILVIO PADILHA R INCONFIDENTES, 20
00006 ROBERTO ARRUDA R BAHIA, 140
00007 SILVANA FERREIRA AV 7 DE SETEMBRO, 2740
00008 CLAUDIA MARCONDES R SALVADOR DALI, 430
00009 JULIANO AGUILERA R PORTUGAL, 121
00010 MARIANA SOARES R CANDIDO PORTINARI, 1340

10 rows selected.

29
Programação de SGBD

SQL> SELECT * FROM Conta;

NRCONTA DTABERTUR CDAG


--------- --------- ----
1021 10-AUG-95 0040
1022 01-SEP-95 0060
1023 12-SEP-95 0040
1024 23-OCT-95 0050
1025 25-OCT-95 0060
1026 04-NOV-95 0050
1027 10-NOV-95 0040
1028 12-NOV-95 0050
1029 17-NOV-95 0050
1030 26-NOV-95 0040
1031 08-DEC-95 0060
1032 12-DEC-95 0060

12 rows selected.

SQL> SELECT * FROM CliConta;

CDCLI NRCONTA
----- ---------
00001 1021
00002 1022
00003 1023
00004 1023
00005 1024
00006 1025
00007 1026
00002 1027
00008 1028
00004 1029
00009 1030
00010 1031
00001 1031
00005 1032

14 rows selected.

30
Programação de SGBD

SQL> SELECT * FROM Movimento;

NRMOV NRCONTA DTMOV T VRMOV DSMOV


--------- --------- --------- - --------- --------------------
1 1021 10-AUG-95 C 500 DEPOSITO
2 1022 20-SEP-95 C 750 DEPOSITO
3 1021 20-SEP-95 D 120 CHEQUE
4 1023 22-SEP-95 C 400 DEPOSITO
5 1024 10-OCT-95 C 920 DEPOSITO
6 1024 12-OCT-95 D 53 ORDEM DE PAGTO
7 1025 19-OCT-95 C 2100 DEPOSITO
8 1026 10-NOV-95 C 535 DEPOSITO
9 1021 12-NOV-95 D 100 CHEQUE
10 1022 20-NOV-95 D 80 CHEQUE
11 1027 10-DEC-95 C 100 DEPOSITO
12 1028 15-DEC-95 C 1500 DEPOSITO
13 1022 18-DEC-95 C 50 DEPOSITO
14 1023 18-DEC-95 D 250 CHEQUE
15 1029 19-DEC-95 C 1050 DEPOSITO
16 1030 20-DEC-95 C 500 DEPOSITO
17 1031 22-DEC-95 C 50 DEPOSITO
18 1021 22-DEC-95 C 50 DEPOSITO
19 1024 23-DEC-95 C 180 DEPOSITO
20 1021 27-DEC-95 C 238 DEPOSITO

20 rows selected.

Ao analisar a composição de cada tabela, esperamos que você esteja imaginando como foram
escritos os comandos INSERT correspondentes. Você também deve ter notado que o saldo de uma conta
corrente não se encontra disponível explicitamente em lugar algum, mas pode ser obtido através de um
cálculo. Essa solução é possível mas não é muito prática. Suponha que queiramos acrescentá-la, de maneira
que a recuperação desse valor não seja muito demorada. O melhor lugar para ela seria na tabela de
movimentos, conforme indicado na nova versão de nosso modelo Entidade / Relacionamento, mostrado a
seguir. O valor correspondente ao saldo deverá ser calculado a cada alteração na base de dados e colocado
nesse novo campo, que mostrará assim o saldo atual da conta após computado o movimento.
Evidentemente, o conteúdo do campo saldo não deverá ser nulo em hipótese alguma para qualquer
movimento conhecido em qualquer conta.

31
Programação de SGBD

@ Núm. Conta
Data Abertura
(E) Agência

Conta
Cliente Mantém Possui Movimento
Corrente

@ Cód. Cliente (E) Cliente @ Núm. Mov.


Nome (E) Conta Corr. (E) Conta Corr.
Endereço Data Movim.
Telefone Tipo Mov.
Pertence
CPF Valor Movim.
RG Descr. Movim.
Saldo Atual

Agência

@ Núm. Agênc.
Nome Agência
Endereço
Telefone

A implementação da alteração em nossa base de dados será feita, em princípio, através de um


comando ALTER TABLE. Entretanto, devido ao fato de que o valor da nova coluna deverá ser calculado e
que a tabela já conterá dados no momento em que for alterada, um certo plano de ação se impõe de forma
que algumas questões importantes sejam resolvidas no processo de atualização da base de dados:

a) o novo campo deverá ser criado inicialmente sem a cláusula NOT NULL, uma vez que o valor do
saldo para cada linha da tabela de movimentos será calculado posteriormente;
b) deverá ser criado um procedimento que percorra a tabela de movimentos de ponta a ponta e
atualize os saldos com os valores corretos;
c) atualizada a coluna de saldo para as linhas pré-existentes na tabela, será necessário emitir um
novo comando ALTER TABLE, para acrescentar a cláusula NOT NULL à nova coluna;
d) finalmente, deverá ser criado um trigger de banco de dados, de maneira que o valor
correspondente ao saldo seja calculado a cada alteração na tabela de movimentos.

32
Programação de SGBD

Basicamente, os comandos a serem emitidos são os seguintes:

SQL> ALTER TABLE Movimento ADD ( VrSaldo NUMBER( 12, 2 ) );

Table altered.

SQL>

Esse comando cria a nova coluna na tabela. O script a seguir cria o procedimento que ficará
armazenado no SGBD e quando ativado, calculará e atualizará o valor do saldo para cada movimento.

-- *
-- * Arquivo: CriProc0.sql
-- * Autor: Cesar
-- * Data: 2/9/97
-- * Finalidade: Exemplo de procedimento de banco de dados
-- * (no caso, o procedimento AtuSaldoMov p/ atualizar
-- * o saldo de uma conta na tabela de movimentos)
-- *

CREATE OR REPLACE PROCEDURE AtuSaldoMov AS


-- *
-- * Atualiza a coluna do saldo da conta na tabela de movimentos
-- *

-- * Declaracoes das variaveis


vNrConta Conta.NrConta%TYPE ;
vNrMov Movimento.NrMov%TYPE ;
vTpMov Movimento.TpMov%TYPE ;
vVrMov Movimento.VrMov%TYPE ;
vVrSaldo Movimento.VrSaldo%TYPE ;

-- *
-- * Cursores p/ obter as contas a processar e seus movimentos
-- *
CURSOR CursorContas IS
SELECT Conta.NrConta
FROM Conta
ORDER BY Conta.NrConta ;

CURSOR CursorSaldoMov IS
SELECT Movimento.NrMov, Movimento.TpMov, Movimento.VrMov
FROM Movimento
WHERE Movimento.NrConta = vNrConta
ORDER BY Movimento.NrMov ;

-- * Corpo do procedimento
BEGIN
OPEN CursorContas ;

33
Programação de SGBD

LOOP
FETCH CursorContas INTO vNrConta ;
EXIT WHEN CursorContas%NOTFOUND ;
vVrSaldo := 0 ;

-- * Atualiza o novo campo na tabela Conta


OPEN CursorSaldoMov ;

LOOP
FETCH CursorSaldoMov INTO vNrMov, vTpMov, vVrMov
;
EXIT WHEN CursorSaldoMov%NOTFOUND ;

IF vTpMov = 'C' THEN


vVrSaldo := vVrSaldo + vVrMov ;
ELSE
vVrSaldo := vVrSaldo - vVrMov ;
END IF;

UPDATE Movimento
SET Movimento.VrSaldo = vVrSaldo
WHERE Movimento.NrMov = vNrMov ;
END LOOP ;

CLOSE CursorSaldoMov ;
END LOOP ;

CLOSE CursorContas ;
END AtuSaldoMov ;

-- *
-- * Fim do procedimento
-- *

O script cria no servidor de banco de dados uma rotina do tipo procedure chamada AtuSaldoMov.
Ela utiliza dois cursores, um para obter os números de conta corrente a processar e outro para obter os
dados dos movimentos da conta. O valor do saldo é calculado com base no saldo anterior, tipo de
movimento e valor do movimento. O resultado é colocado na coluna do saldo através de um comando
UPDATE. Dois loopings são necessários nessa lógica: um para processar todas as contas, outro para
processar todos os movimentos de uma conta. A lógica utilizada é relativamente simples, mas a execução
desse procedimento pode ser demorada, pois ele causa um grande tráfego na rede. Felizmente, para
finalidades práticas, essa rotina deverá ser executada apenas uma vez mesmo. O comando para que ela seja
executada seria o seguinte no prompt do SQL *Plus:

SQL> EXEC AtuSaldoMov

PL/SQL procedure successfully completed.

SQL>

34
Programação de SGBD

Com a tabela atualizada, vamos então tornar obrigatório o fornecimento de um valor para a coluna do
saldo.

35
Programação de SGBD

SQL> ALTER TABLE Movimento MODIFY ( VrSaldo NOT NULL ) ;

Table altered.

SQL>

Resta então criar o trigger de banco de dados para que o valor de saldo seja calculado a cada
alteração ou inclusão na tabela de movimentos. Isso é feito através do script a seguir, que implementa a
lógica apenas para o caso de inclusão na tabela, ficando como exercício alterá-lo para que preveja também a
possibilidade de alteração.

-- *
-- * Arquivo: CriTrig1.sql
-- * Autor: Cesar
-- * Data: 30/3/97
-- * Finalidade: Criar um exemplo de gatilho de banco de dados
-- *

CREATE OR REPLACE TRIGGER AtuSaldoInclusao


-- *
-- * Calcula o novo saldo com base no saldo anterior e no valor
-- * e tipo do movimento atual
-- *

-- * Instrucao de gatilho
BEFORE INSERT ON Movimento

-- * Linha de disparo
FOR EACH ROW

-- * Declaracoes de dados
DECLARE
-- * Variavel p/ armazenar o saldo original da conta
VrSaldoAnterior Movimento.VrSaldo%TYPE ;

-- * Cursor p/ obter o saldo original da conta


CURSOR CursorSaldoMov IS
SELECT VrSaldo
FROM Movimento
WHERE Movimento.NrConta = :new.NrConta
ORDER BY Movimento.NrMov DESC ;

-- * Corpo do gatilho
BEGIN
-- * Abrindo o cursor
OPEN CursorSaldoMov ;

-- * Obtendo a 1a linha (c/ movimento mais recente da conta)


FETCH CursorSaldoMov INTO VrSaldoAnterior ;

36
Programação de SGBD

IF CursorSaldoMov%NOTFOUND THEN
-- * Primeiro movimento na conta, saldo original e
zero VrSaldoAnterior := 0 ;
END IF ;

-- * Fechando o cursor
CLOSE CursorSaldoMov ;

-- * Calculando o novo saldo


IF :new.TpMov = 'C' THEN
:new.VrSaldo := VrSaldoAnterior + :new.VrMov ;
ELSE
:new.VrSaldo := VrSaldoAnterior - :new.VrMov ;
END IF ;

END AtuSaldoInclusao ;

-- *
-- * Fim deste script
-- *

37
Programação de SGBD

Exercício proposto

Agora, vamos assumir que estudos concluíram ser interessante desnormalizar a base de dados,
colocando o valor do saldo também na tabela de conta correntes. Essa alteração tornaria mais simples a
operação de consulta ao saldo de uma conta, minimizando o tráfego na rede e a carga no servidor, mas
acrescentando uma maior complexidade nas operações de atualização da tabela de movimentos, pois o novo
valor do saldo deve ser atualizado também na tabela de contas. Assim, descreva o procedimento, os
comandos e as rotinas a serem criadas para implementar esse novo requisito. Como auxílio, fornecemos o
novo modelo Entidade / Relacionamento.

@ Núm. Conta
Data Abertura
(E) Agência
Saldo Atual

Conta
Cliente Mantém Possui Movimento
Corrente

@ Cód. Cliente (E) Cliente @ Núm. Mov.


Nome (E) Conta Corr. (E) Conta Corr.
Endereço Data Movim.
Telefone Tipo Mov.
Pertence
CPF Valor Movim.
RG Descr. Movim.
Saldo Atual

Agência

@ Núm. Agênc.
Nome Agência
Endereço
Telefone

38
Programação de SGBD

Funções pré-definidas

ABS( <expN> )

Retorna o valor absoluto (número sem sinal) de expN.

Exemplo:

SELECT ABS( -1 ) FROM Dual ; /* Resultado: 1 */

ADD_MONTHS( <expD>, <expN> )

Retorna a data expD acrescida de expN meses.

Exemplo:

SELECT ADD_MONTHS(SYSDATE, 6 ) FROM Dual ; /* Resultado: 16-SEP-98 */

ASCII( <expC> )

Retorna o valor ASCII do primeiro caracter de expC.

Exemplo:

SELECT ASCII(CdDepto) FROM Depto


WHERE CdDepto = ‘D1’ ; /* Resultado: 68 */

AVG([ DISTINCT | ALL] <expN> )

Retorna a média de expN. Os valores nulos são ignorados.

Exemplo:

SELECT AVG(VrSalario) FROM Funcionaro ; /* Resultado: 1222*/

CEIL( <expN> )

Retorna o maior inteiro maior que ou igual a expN.

Exemplo:

SELECT CEIL(12.33) FROM DUAL ; /* Resultado: 13*/

CHARTOROWID(<expC> )

39
Programação de SGBD

Retorna a expC convertido em um valor de ROWID.


O conteúdo da expC deve estar no formato da pseudo-coluna ROWID.

Exemplo:

SELECT CHARTOROWID(NrMatric) FROM Funcionaro


WHERE NrMatricCDFUNC = ‘1001’ ; /*Resultado: 0001001.0000.0000*/

CHR( <expN> )

Retorna o caracter ASCII referente ao valor expN.

Exemplo:

SELECT CHR(75) FROM DUAL ; /* Resultado: K*/

COUNT([DISTINCT | ALL] {* I <exp> })

Retorna o número de linhas da exp (ignora os valores nulos)


O * retorna o número de todas as linhas incluindo os valores nulos.

Exemplo:

SELECT COUNT( * ) FROM Funcionario ; /* Resultado: 21 */

CONVERT( <expC> [, destino[,fonte]])

Converte expC referente ao conjunto de caracteres fonte para o conjunto de caracteres destino.

Conjuntos disponíveis para destino e fonte:


US7ASCII Default, conjunto de caracteres ASCII US 7-bits.
WE8DEC Conjunto de caracteres ASCII 8 bit-DEC.
WE8HP Conjunto de caracteres ASCII 8 bit-HP.
F7DEC Conjunto de caracteres ASCII 7 bit DEC frança.
WEIBMPC Conjunto de caracteres ASCII 8-bit do IBM PC.

Exemplo:

SELECT CONVERT(‘convertido’, US7ASCII, ‘WE8HP’ ) FROM Dual ;


/* Resultado: convertido */

DECODE ( <exp> , <exp1> , <exp2>, [,<exp3>])

Caso a exp seja igual a exp1 retorna exp2, caso contrário, retorna exp3, se exp3 for omitido, retorna
nulo se nenhuma condição for satisfeita.

40
Programação de SGBD

Exemplo:

SELECT DECODE(CdDepto, ‘D1’, ‘ADMINISTRACAO’, ‘NOT OK’) FROM Depto


WHERE CDDepto = ‘D1’ ;
/* Resultado: ADMINISTRACAO */

DUMP( <exp> [,formato[, expl1[, expl2]]])

Exibe o valor da exp em um formato interno especificado pelo parâmetro formato, começando na
posicão expl1, com tamanho de expl2. Formato : Typ=n Len=m: lista de valore caracter a caracter ou dígito a
dígito.
n Tipo de exp
1=Coluna de tipo de dado caractere ;
2=Coluna de tipo de dado numérico ;
12=Coluna de tipo de dado data e hora ;
23=Coluna de tipo de dado RAW ;
96= Sequência de caracteres.
m Tamanho de exp em caracteres ou dígitos (sem o ponto decimal);

Exemplo:

SELECT DUMP(NrMatric,4) OCTAL FROM Funcionario


WHERE CdDepto = 'D1' AND CdCargo = 'C1';
/* Resultado: Typ=96 Len=4: 54,49,54,54 */

41
Programação de SGBD

FLOOR( <expN> )

Retorna o menor inteiro igual a ou menor que expN.

Exemplo:

SELECT FLOOR( 12.8 ) FROM Dual ; /* Resultado: 12 */

GREATEST( <exp>,...)

Retorna o maior valor de uma lista de valores exp.

Exemplo:

SELECT GREATEST(‘abcd’, 123, ‘defg’ ) FROM Dual ; /* Resultado: defg */

INITICAP( <expC> )

Retorna a expC com a primeira letra de cada palavra em maiúscula e o resto da palavra em minúsculas.

Exemplo:

SELECT INITICAP(NmFunc) FROM Funcionario


WHERE NrMatric = ‘1004’ ; /* Resultado: Lucio Torres */

INSTR( <expC1>, <expC2> [, <expI> [,expI]])

Retorna a posição da n-ésima ocorrência de expC2 dentro de expC1, começando a pesquisa na


posição expN1 e qual ocorrência expN2 a ser encontrada
Exemplo:

SELECT NmFunc, INSTR( NmFunc, ‘A’, 10, 1) FROM Funcionario


WHERE CdFunc = ‘1001’; /* Resultado: 10 */

LAST_DAY( <expD> )

Retorna a data do último dia do mês contida na data expD.

Exemplo:

SELECT LAST_DAY( SYSDATE ) FROM Dual ; /* Resultado: 31-MAR-98 */

42
Programação de SGBD

LEAST( <exp>,... )

Retorna o menor valor de uma lista de valores exp.

Exemplo:

SELECT LEAST( ‘abc’,123,’defg’ ) FROM Dual ; /* Resultado: 123 */

LENGTH( <expC> )

Retorna o número de caracteres em expC

Exemplo:

SELECT LENGTH( NmDepto ) FROM Depto


WHERE CdDepto = ‘D1’ ; /* Resultado: 13 */

LOWER( <expC> )

Retorna todas as letras da expC em minúsculas.

Exemplo:

SELECT LOWER( NmCargo ) FROM Cargo


WHERE CdCargo = ‘C1’; /* Resultado: cozinheira */

LPAD( <expC1> , <expI>[, <expC2>])

Retorna a expC1 preenchido à esquerda com exprI caracteres de expC2. Se expC2 for omitido, retorna
preenchido em brancos.

Exemplo:

SELECT LPAD( ‘valor’,8, ‘*’) FROM Dual ; /* Resultado: ***valor */

LTRIM( <expC1> [, <expC2>] )

Elimina todos os caracteres à esquerda

Exemplo:

SELECT ABS( -1 ) FROM Dual ; /* Resultado: 1 */

43
Programação de SGBD

MAX( [ DISTINCT | ALL ] <exp> )

Retorna o maior valor de exp.

Exemplo:

SELECT MAX( VrSalario ) FROM Funcionario ; /* Resultado: */

MIN( [ DISTINCT | ALL ] <exp> )

Retorna o menor valor de exp.

Exemplo:

SELECT MIN( VrSalario ) FROM Funcionario ; /* Resultado: 1 */

MOD( <expN1>, <expN2> )

Retorna o resto da divisão de expN1 por expN2

Exemplo:

SELECT MOD( 10, 3 ) FROM Dual ; /* Resultado: 1 */

MONTHS_BETWEEN( <expD1>, <expD2> )

Retorna o número de meses entre as datas expD1 e expD2.

Exemplo:

SELECT MONTHS_BETWEEN( ‘20-FEB-91’, ’01-JAN-91’ ) FROM Dual ;


/* Resultado: 1.6129 */

NEW_TIME( <expD>, <expC1>, <expC2> )

Retorna a data e a hora da zona expC2 quando a data e a hora da zona expC1 estiver na data e hora
expD.

Exemplo:

SELECT TO_CHAR( NEW_TIME(TO_DATE(‘20:16’,hh24:mi),‘PST’,‘GMT’),


’hh24:mi’ ) FROM Dual ; /* Resultado: 04:16 */

44
Programação de SGBD

NEXT_DAY( <expD>, expC )

Retorna a data do primeiro dia da semana expC igual a ou posterior a expD.

Exemplo:

SELECT NEXT_DAY( ‘27-OCT-94’, ‘SUNDAY’ ) FROM Dual ;


/* Resultado: 30-OCT-94 )*/

NLSSORT( <expC> )

Retorna um valor sequencial de expC referente a linguagem nacional utilizada pelo sistema. Quando a
ordem sequencial é baseada em valores numéricos dos caracteres. Esta ordem é chamada ordem binária.

Exemplo:

SELECT NLSSORT( ‘A’ ) FROM Dual ; /* Resultado: 4100 */

NVL( <exp1>, <exp2> )

Retorna exp2 caso exp1 seja nulo, senão retorna exp1.

Exemplo:

SELECT NVL( VrSalario, 0 ) FROM Funcionario


WHERE CdDepto = ‘D2’ ;

POWER( <expN>, <n> )

Retorna o valor expN elevado a n-ésima potência.

Exemplo:

SELECT POWER( 2, 6 ) FROM Dual ; /* Resultado: 64 */

45
Programação de SGBD

REPLACE( <expC1>, <expC2>[, <expC3>] )

Retorna expC1 com todas as ocorrências de caracteres de expC2, substituidas pelos caracteres de
expC3. Se expC3 for omitido , elimina todas a ocorrências de expC2.

Exemplo:

SELECT REPLACE( ‘ROMEU’, ‘EU’, ‘ARIA’ ) FROM Dual ;


/* Resultado: ROMARIA*/

ROUND( <expD> [, formato ])

Retorna a expD arrendondado como especificado pelo parâmetro formato

Exemplo:

SELECT ROUND( TO_DATE( ‘27-OCT-97’), ‘YEAR’) FROM Dual ;


/* Resultado: 01-JAN-98 */

ROUND( <expN> [, <expI> ] )

Retorna a expN arrendondado para a expI casas decimais; se expI for negativo, arredonda a direita do
ponto decimal, se expI for zero, arredonda para o valor inteiro mais próximo.

Exemplo:

SELECT ROUND( 153.452, 2) FROM DUAL ; /* Resultado: 153.45 */


SELECT ROUND( 153.452, -2) FROM DUAL ; /* Resultado: 200 */
SELECT ROUND( 153.452) FROM DUAL ; /* Resultado: 153 */

ROWIDTOCHAR( <ROWID> )

Converte o valor ROWID para um valor tipo caracter.

Exemplo:

SELECT ROWID FROM Funcionario


WHERE ROWIDTOCHAR(ROWID) LIKE ‘%5AE1’%’; /* Resultado:no rows selected */

46
Programação de SGBD

RPAD( <expC1>, <expI> [,<expC2> )

Retorna expC1 preenchido à direita com expI caracteres de expC2. Se expC2 for omitido, retorna
preenchido com branco.

Exemplo:

SELECT RPAD( ‘VALOR’, 8, ‘*’ ) FROM Dual ; /* Resultado: VALOR*** */

RTRIM( <expC1> [,<expC2> )

Elimina todos os caracteres à esquerda de expC1 iguais ao primeiro caracter de expC2. Se expC2 for
omitido, elimina os caracteres em branco.

Exemplo:

SELECT RTRIM( ‘ABCD999’, ‘9’ ) FROM Dual ; /* Resultado: ABCD */

SIGN( <expN> )

Se expN for negativo retorna -1, se for positivo retorna 1, se for zero retorna 0.

Exemplo:

SELECT SIGN( 5432 ) FROM Dual ; /* Resultado: 1 */

SOUNDEX( <expC> )

Retorna uma representação fonética de cada palavra em expC (usa fonética da lingua inglesa).

Exemplo:

SELECT * FROM Depto WHERE SOUNDEX( NmDepto ) = SOUNDEX( 'VEMDIS') ;

/* Resultado: D4 VENDAS */

SQRT( <expN> )

Retorna a raiz quadrada da expN, se expN for negativa, retorna nulo.

Exemplo:

SELECT SQRT( 144 ) FROM Dual ; /* Resultado: 12 */

47
Programação de SGBD

STDDEV ([ DISCTINT | ALL<expN>)

Retorna o desvio padrão de expN. Valores nulos são ignorados

Exemplo:

SELECT STDDEV( VrSalario ) FROM Funcionario ; /* Resultado: 555.90267*/

SUBSTR( <expC> , <expI1> [,<expI2>)

Retorna a parte de expC, começando na posição expI1 com tamanho de expI2.

Exemplo:

SELECT SUBSTR( ‘ABCDF’, 2,3 ) FROM Dual; /* Resultado: BCD */

SUM( [DISTINCT | ALL] <expN> )

Retorna a soma dos valores de expN. Valores nulos são ignorados.

Exemplo:

SELECT SUM( VrSalario ) FROM Funcionario ; /* Resultado: 7750 */

TO_CHAR( <expN> [, formato ])

Converte o valor expN de tipo de dado numérico para um valor do tipo de dado caracter no formato
especificado pelo parâmetro formato.

Exemplo:

SELECT TO_CHAR( 194 , ‘09999’ ) FROM Dual ; /* Resultado: 00194 */

TO_CHAR( <expD> [, formato ])

Converte o valor expD de tipo de dado DATE para um valor do tipo de dado caracter no formato
especificado pelo parâmetro formato.

Exemplo:

SELECT TO_CHAR( DtAdm, ‘DD’ )


FROM Funcionario
WHERE NrMatric = ‘1023’ ; /* Resultado: 12 */

SELECT TO_CHAR( SYSDATE, ‘DD/MON/YYYY HH:MM:SS’ )


FROM Dual ; /* Resultado: 17/SEP/2002 10:09:03 */

48
Programação de SGBD

TO_DATE( <expC> [, formato ])

Converte a data/hora expC que esta no tipo de dado caracter formato especificado pelo parâmetro
formato, para um valor no tipo de dado DATE.

Exemplo:

SELECT TO_DATE( ‘15/JAN/98’ ) FROM Dual ;/* Resultado:15-JAN-98*/

TO_NUMBER( <expC> )

Converte um número do tipo caracter para o tipo de dado numérico.

Exemplo:

SELECT TO_NUMBER( SUBSTR(‘ABCD00193’,5,5) )


FROM Dual ; /* Resultado: 193 */

TRANSLATE( <expC1> , <expC2>, <expC3>)

Retorna expC1 substituido com todas as ocorrências de expC2 com o caracter correspondente em
expC3.

Exemplo:

SELECT TRANSLATE( ‘MARIO’, ‘O’,‘A’ ) FROM Dual ;/* Resultado: */

TRUNC( <expN> [, n ] )

Retorna expN truncado na n-ésima casa decimal; se for omitido ou for zero, trunca para o valor
inteiro, se n for negativo , zera o dígito à esquerda do ponto decimal.

Exemplo:

SELECT TRUNC( 785.143 ) FROM Dual ; /* Resultado: 785 */


SELECT TRUNC( 785.14, -2 ) FROM Dual ; /* Resultado:100 */

49
Programação de SGBD

TRUNC( <expD> [, formato ] )

Retorna expD truncado como especificado no parâmetro formato.

Exemplo:

SELECT TRUNC( 785.143 )


FROM Dual ; /* Resultado: 785 */

UPPER( <expC> )

Retorna a expC com todas as letras em maiúsculas

Exemplo:

SELECT UPPER( NmDepto )


FROM Depto
WHERE CDDEPTO = ‘D1’; /* Resultado: ADMINISTRACAO */

USERENV( <opção> )

Retorna informações da opção escolhida sobre o usuário e/ou a seção corrente.

Opções :
ENTRYID Identificador de entrada
SESSIONID Identificador de seção
TERMINAL Identificador do terminal do usuário
LANGUAGE Linguagem em uso

Exemplo:

SELECT USERENV( ‘TERMINAL’ )


FROM Dual ; /* Resultado: WINDOWS95 PC */

50
Programação de SGBD

VARIANCE( [ DISTINCT | ALL ] <expN> )

Retorna a variação de expN. Os valores nulos são ignorados.

Exemplo:

SELECT VARIANCE( VrSalario )


FROM Funcionario ; /* Resultado: 309027.78 */

VSIZE( <exp> )

Retorna o número de bytes usados para armazenar internamente exp.

Exemplo:

SELECT NmFunc, VSIZE( NmFunc )


FROM Funcionario
WHERE NrMatric = ‘1001’; /* Resultado: 12 */

Conversão de tipo de dados

Para Caracter Número Data


De
Caracter - TO_NUMBER TO_DATE
Número TO_CHAR - TO_DATE
Data TO_CHAR ERRO -

51

Você também pode gostar