Escolar Documentos
Profissional Documentos
Cultura Documentos
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:
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.
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).
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:
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
Procedimentos
Exemplo:
Ativação Procedimento
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
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
Neste material utilizaremos o termo Trigger para referenciar os gatilhos de banco de dados.
4
Programação de SGBD
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:
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.
É 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
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
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.
9
Programação de SGBD
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.
10
Programação de SGBD
É 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.
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 ;
...
11
Programação de SGBD
12
Programação de SGBD
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
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.
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.
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.
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.
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:
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
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
%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.
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:
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
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.
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 ;
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
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:
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
23
Programação de SGBD
Exemplo 2:
Ativação Trigger
Exemplo 3:
Ativação Trigger
Exemplo 4:
Ativação Trigger
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.
@ Núm. Conta
Data Abertura
(E) Agência
Conta
Cliente Mantém Possui Movimento
Corrente
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 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
-- *
-- *
-- * 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:
3 rows selected.
10 rows selected.
29
Programação de SGBD
12 rows selected.
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
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
Agência
@ Núm. Agênc.
Nome Agência
Endereço
Telefone
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
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)
-- *
-- *
-- * 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 ;
LOOP
FETCH CursorSaldoMov INTO vNrMov, vTpMov, vVrMov
;
EXIT WHEN CursorSaldoMov%NOTFOUND ;
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>
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
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
-- *
-- * 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 ;
-- * Corpo do gatilho
BEGIN
-- * Abrindo o cursor
OPEN CursorSaldoMov ;
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 ;
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
Agência
@ Núm. Agênc.
Nome Agência
Endereço
Telefone
38
Programação de SGBD
Funções pré-definidas
ABS( <expN> )
Exemplo:
Exemplo:
ASCII( <expC> )
Exemplo:
Exemplo:
CEIL( <expN> )
Exemplo:
CHARTOROWID(<expC> )
39
Programação de SGBD
Exemplo:
CHR( <expN> )
Exemplo:
Exemplo:
Converte expC referente ao conjunto de caracteres fonte para o conjunto de caracteres destino.
Exemplo:
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:
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:
41
Programação de SGBD
FLOOR( <expN> )
Exemplo:
GREATEST( <exp>,...)
Exemplo:
INITICAP( <expC> )
Retorna a expC com a primeira letra de cada palavra em maiúscula e o resto da palavra em minúsculas.
Exemplo:
LAST_DAY( <expD> )
Exemplo:
42
Programação de SGBD
LEAST( <exp>,... )
Exemplo:
LENGTH( <expC> )
Exemplo:
LOWER( <expC> )
Exemplo:
Retorna a expC1 preenchido à esquerda com exprI caracteres de expC2. Se expC2 for omitido, retorna
preenchido em brancos.
Exemplo:
Exemplo:
43
Programação de SGBD
Exemplo:
Exemplo:
Exemplo:
Exemplo:
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:
44
Programação de SGBD
Exemplo:
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:
Exemplo:
Exemplo:
45
Programação de SGBD
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:
Exemplo:
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:
ROWIDTOCHAR( <ROWID> )
Exemplo:
46
Programação de SGBD
Retorna expC1 preenchido à direita com expI caracteres de expC2. Se expC2 for omitido, retorna
preenchido com branco.
Exemplo:
Elimina todos os caracteres à esquerda de expC1 iguais ao primeiro caracter de expC2. Se expC2 for
omitido, elimina os caracteres em branco.
Exemplo:
SIGN( <expN> )
Se expN for negativo retorna -1, se for positivo retorna 1, se for zero retorna 0.
Exemplo:
SOUNDEX( <expC> )
Retorna uma representação fonética de cada palavra em expC (usa fonética da lingua inglesa).
Exemplo:
/* Resultado: D4 VENDAS */
SQRT( <expN> )
Exemplo:
47
Programação de SGBD
Exemplo:
Exemplo:
Exemplo:
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:
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:
48
Programação de SGBD
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:
TO_NUMBER( <expC> )
Exemplo:
Retorna expC1 substituido com todas as ocorrências de expC2 com o caracter correspondente em
expC3.
Exemplo:
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:
49
Programação de SGBD
Exemplo:
UPPER( <expC> )
Exemplo:
USERENV( <opção> )
Opções :
ENTRYID Identificador de entrada
SESSIONID Identificador de seção
TERMINAL Identificador do terminal do usuário
LANGUAGE Linguagem em uso
Exemplo:
50
Programação de SGBD
Exemplo:
VSIZE( <exp> )
Exemplo:
51