PL SQL
PL SQL
Blocos PL-SQL
DECLARE – opcional
BEGIN – obrigatório
instruções SQL
instruções PL-SQL
EXCEPTION – opcional
END; - obrigatório
Exemplo:
DECLARE
v_variable varchar2(5);
BEGIN
select column_name
into v_variable
from table_name;
EXCEPTION
when exception_name then ...
END;
É necessário declarar todos os identificadores PL-SQL na seção de declaração antes de referenciá-los no bloco PL-SQL.
Sintaxe
Exemplos
v_hiredate DATE;
v_deptno NUMBER(2) NOT NULL := 10;
v_location VARCHAR2(13) := 'Erechim';
c_comm CONSTANT NUMBER :=1400;
Krishnamurthy, U. Oracle Database SQL Language Quick Reference. Oracle Corporation Press, 2022. Disponível em:
<https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlqr/sql-language-quick-reference.pdf>. Acesso em: 21 mar.
2024.
Kochhar, N.; Gravina, E.; Nathan, P. Introduction to Oracle – SQL and PL-SQL - NT. Oracle Corporation Press, 2000.
Morin, L. Database PL/SQL Language Reference. Oracle Corporation Press, 2023. Disponível em:
<https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/database-pl-sql-language-reference.pdf>. Acesso em: 21
mar. 2024.
select sal * 0.10
into v_bonus
from emp
where empno = 7369;
A palavra-chave DEFAULT também pode ser usada em vez do operador de atribuição para inicializar variáveis.
O atributo %type
Serve para declarar uma variável como sendo do tipo definido de uma coluna do banco de dados ou outra variável previamente
declarada.
Exemplo
...
v_ename emp.ename%type;
v_balance number(7,2);
v_min_balance v_balance%type;
...
Com este tipo de variável é possível armazenar blocos de dados não estruturados, como textos, imagens, videoclips e formatos
de arquivos para armazenar sons.
Podem ser
CLOBs: sigla para Character Large Object. É um tipo de dado usado em bancos de dados para armazenar grandes quantidades
de texto, como documentos, arquivos XML, logs ou qualquer conteúdo textual que exceda o tamanho suportado por tipos de
dados comuns, como VARCHAR ou CHAR.
BLOBs: Binary Large Object é um tipo de dado usado em bancos de dados para armazenar grandes quantidades de dados
binários, como imagens, vídeos, áudios, documentos e outros arquivos não estruturados. Diferente de tipos de dados
tradicionais como VARCHAR ou TEXT, um BLOB pode armazenar dados em um formato bruto (raw), sem precisar de
conversão para texto.
BFILEs: tipos de dados específicos usados para armazenar referências a arquivos binários que estão fora do banco de dados,
geralmente no sistema de arquivos do servidor. Diferente de um BLOB, que armazena os dados diretamente dentro do banco, o
BFILE apenas guarda um ponteiro (caminho do arquivo) para o arquivo armazenado externamente.
NCLOB: National Character Large Object é um tipo de dado usado em bancos de dados Oracle para armazenar grandes
quantidades de texto em formatos de caracteres (NCHAR/ NVARCHAR2). Ele é similar ao CLOB (Character Large Object),
mas projetado para armazenar texto multilíngue, suportando conjuntos de caracteres internacionais.
Sintaxe
SELECT select_list
INTO {variable_name[, variable_name]... | record_name}
FROM tabela
WHERE condição;
Exercício 1
Construir um bloco PL-SQL que retorna o número do departamento e sua localização.
Obs.: Todos exercícios desta apostila de PL-SQL usarão a conta hr/hr em nosso banco de dados.
sqlplus hr/hr
DECLARE
v_deptno number(2);
v_loc varchar2(15);
BEGIN
select deptno, loc
into v_deptno, v_loc
from dept
where dname = 'SALES';
END;
/
Exercício 2
Construir um bloco PL-SQL que retorna o número do departamento e sua localização. Mostrar também esta informação.
set serverout on
DECLARE
v_deptno number(2);
v_loc varchar2(15);
BEGIN
select deptno, loc
into v_deptno, v_loc
from dept
where dname = 'SALES';
Exercício 3
Recuperar o total de salários de todos os funcionários no departamento especificado.
DECLARE
v_sum_sal emp.sal%type;
v_deptno number not null := 10;
BEGIN
select sum(sal)
into v_sum_sal
from emp
where deptno = v_deptno;
END;
/
Para resolver os exercícios abaixo, primeiro crie comandos SQL que retornem as informações solicitadas, depois transforme
isso em blocos PL-SQL.
Exercício 3.A
Mostrar o número do funcionário, seu nome e seu cargo daquele funcionário que possui o maior número/código de
funcionário.
Exercício 3.B
Mostrar o nome do departamento e a localização do departamento do funcionário que foi contratado mais recentemente.
Exercício 3.C
Mostrar o nome, o cargo, o salário e a localização do funcionário que tem salário entre 4.000 e 6.000.
Passo 1: mostrar o nome, cargo, salário e código de departamento do funcionário com salário no intervalo.
Passo 2: mostrar a localização do departamento.
Passo 3: transformar isso em bloco PL-SQL.
Talvez você não tenha reparado, mas os blocos PL-SQL dos exercícios anteriores só funcionaram porque as consultas
retornavam somente uma linha. Para consultas que retornam mais de uma linha faremos uso dos chamados cursores, que
veremos mais adiante. Mais de uma ou nenhuma linha geram erro. No bloco PL-SQL pode-se tratar essas exceções de erro.
Neste caso, os erros em questão a serem tratados seriam NO_DATA_FOUND e TOO_MANY_ROWS.
Inserindo dados
Inserir um funcionário:
BEGIN
insert into emp ( empno, ename, job, deptno)
values ( 999, 'HARDING', 'CLERK', 10 );
END;
/
Observe que o bloco anterior não precisaria existir se quiséssemos apenas inserir um registro. Poderíamos ter executado o
INSERT sem o bloco. Mas funciona também. Vamos verificar com
select *
from emp
where empno=999;
Como não há COMMIT no bloco, temos que finalizar a transação depois do bloco PLSQL executado:
commit;
Atualizando dados
Aumentar o salário de todos os funcionários na tabela EMP que sejam analistas:
select *
from emp
where job = 'ANALYST';
DECLARE
v_sal_increase emp.sal%type := 2000;
BEGIN
update emp
set sal = sal + v_sal_increase
where job = 'ANALYST';
END;
/
select *
from emp
where job = 'ANALYST';
commit;
Tenha o cuidado de não executar duas ou mais vezes !!! Caso contrário, o salário será aumentado repetidamente e você terá
que se justificar perante sua chefia, tendo talvez que passar no RH da empresa ao final do dia. Os blocos PLSQL podem ser re-
executados digitando-se “/” seguido de ENTER. Mas tenha sempre cuidado com isso em bancos de dados em “produção”.
Excluindo dados
Excluir a linha que corresponde ao funcionário JAMES na tabela EMP:
select *
from emp
where ename = 'JAMES';
DECLARE
v_ename emp.ename%type := 'JAMES';
BEGIN
delete from emp
where ename = v_ename;
END;
/
select *
from emp
where ename='JAMES';
commit;
Instruções COMMIT e ROLLBACK
Pode-se controlar a lógica das transações com as instruções COMMIT e ROLLBACK. As transações DML são iniciadas e
finalizadas na ocorrência de uma instrução COMMIT ou ROLLBACK. Pode-se também marcar um ponto intermediário no
processo da transação através do uso dos SAVEPOINTs.
select *
from emp
where job = 'ANALYST';
DECLARE
v_sal_increase emp.sal%type := 500;
BEGIN
update emp
set sal = sal + v_sal_increase
where job = 'ANALYST';
commit;
END;
/
select *
from emp
where job = 'ANALYST';
Para facilitar ainda mais sua compreensão sobre eles, criaremos procedimentos, funções e gatilhos para basicamente as
mesmas necessidades dos exercícios anteriores. Você poderia se perguntar: mas nesse caso, se as necessidades anteriores já
foram atingidas com blocos PLSQL escritos em scripts, por que teríamos a necessidade de transformá-los nesses objetos? Tudo
depende da operacionalização. Se blocos PLSQL que serão rotineiramente executados pelo profissional de TI, desenvolvedor
ou DBA já são suficientes, tudo bem. Porém, se isso precisa ser executado a qualquer momento pelo usuário, a aplicação
poderá requisitar a execução do objeto de banco de dados (procedure ou function), sem necessidade de intervenção de outras
pessoas, e nesse caso é claramente mais eficiente ter o código implementado em tais objetos. Por vezes a performance dos
comandos armazenados no banco de dados na forma de procedimentos, funções e gatilhos, também é maior do que quando sua
execução se dá por consoles SQL, já que os objetos armazenados já estão compilados.
Exercício 4
Criar uma procedure que receba como parâmetro um número de departamento e exiba a localização do mesmo.
CREATE OR REPLACE PROCEDURE p_local (v_deptno number) as
v_loc varchar2(15);
BEGIN
select loc
into v_loc
from dept
where deptno = v_deptno;
Se a procedure foi criada, significa que não houve erros de compilação. Se houve erros, digite
Vamos de novo:
Vamos de novo:
SQL> set serverout on
SQL> exec p_local(10)
Exercício 4.A
Criar uma procedure que receba como parâmetro um número de funcionário e mostre seu nome, sua data de contratação e a
cidade onde ele trabalha.
Exercício 4.B
Criar uma procedure que receba como parâmetro um nome de cidade e mostre o nome e o salário do funcionário com o menor
salário daquela cidade.
Obs.: DALLAS: SMITH 800,00. NEW YORK: MILLER 1300,00. CHICAGO: too many rows. SEATTLE e BOSTON não
tem.
Exercício 5
Criar uma função que receba como parâmetro o cargo do funcionário e retorne o menor salário do cargo informado.
RETURN v_menor_sal;
END;
/
Vamos de novo:
Vamos de novo:
select f_menor_sal('ANALYST')
from dual;
Agora, a função f_menor_sal criada anteriormente será usada dentro de um bloco PL-SQL:
select ename, job, sal from emp where job='CLERK';
-- o bloco PLSQL abaixo atualiza todos os salários dos CLERKs para o valor do menor salário entre os CLERKs utilizando a
função recém criada que retornou qual é o menor entre os salários deles
DECLARE
v_menor_sal number(7,2);
BEGIN
v_menor_sal := f_menor_sal('CLERK');
UPDATE emp
SET sal = v_menor_sal
WHERE job = 'CLERK';
COMMIT;
END;
/
Obs.: o texto/código de um objeto compilado fica guardado no banco de dados na view user_source. Para consultar:
SELECT text
FROM user_source
WHERE name = 'F_MENOR_SAL' and
type = 'FUNCTION';
Exercício 5.A
Criar uma função que receba como parâmetro o código de um gerente e devolva a média salarial de todos os funcionários
vinculados ao gerente passado como parâmetro.
Obs.: para fins de teste, o gerente 7698 tem funcionários com média salarial = 1400. O gerente 7839 tem funcionários com
média salarial = 2758,33.
Exercício 6
Queremos manter o registro de alterações salariais realizadas na tabela emp. Para isso criaremos uma tabela auxiliar chamada
ATUALIZACOES_SALARIOS que irá guardar os seguintes dados: empno, ename, sal_antes, sal_depois, data_alteracao
Se você pensar bem, isso poderia ser implementado na própria aplicação que faz uso do BD, mas não é necessário, já que
podemos, para isso, criar o que chamamos de gatilho (trigger). Obs.: a trigger criada usará uma estrutura de controle IF-THEN.
Apesar de ser de fácil compreensão, falaremos um pouco mais sobre isso logo adiante neste documento.
commit;
Note que após o UPDATE acima executamos um COMMIT. Se não tivéssemos feito isso, não teríamos efetivamente
atualizado os salário e também não teríamos as entradas na tabela atualizacoes_salarios. Por vezes, pelas
mais diferentes situações, a transação aborta sem commit e o BD realiza um rollback. Situações para isso
envolvem erros no momento do UPDATE, erros no momento da execução da TRIGGER, erros de comunicação se há
alguma tentativa de acesso remoto por dentro da TRIGGER, etc.
Exercício 7
Vamos dropar a tabela de registro de alterações salariais e ver como uma atualização se comporta.
Obs.: é interessante saber também que o BD, quando você dropou a tabela atualizacoes_salarios, guardou o código da
TRIGGER mas ela estava com status ‘INVALID’. Isso porque o objeto TRIGGER dependia da existência da referida tabela.
Exercício 8
select * from user_dependencies where name = 'T_REGISTRA_SALARIO';
Atributo Descrição
Número de linhas afetadas pela instrução SQL mais
SQL%ROWCOUNT
recente(um valor inteiro)
Atributo booleano avaliado para TRUE se a instrução
SQL%FOUND
SQL mais recente afetar uma ou mais linhas
Atributo booleano avaliado para TRUE se a instrução
SQL%NOTFOUND
SQL mais recente não afetar uma ou mais linhas
Sempre é avaliado para FALSE no caso dos cursores
SQL%ISOPEN implícitos pois o próprio PL-SQL fecha os mesmos
após sua execução
Exercício 9
Excluir da tabela EMP todos os funcionários do departamento 10, mostrando quantas linhas foram excluídas. Usaremos um
comando rollback após a transação pois queremos excluir os funcionários somente para testar o atributo SQL.
rollback;
END;
/
Exercício 10
Criar um bloco PL-SQL que seleciona o número máximo de departamento na tabela DEPT e o exibe.
Exercício 11
Criar um bloco PL-SQL que atualize o departamento de IT da cidade de Seattle para Dallas, mostrando o número de linhas
afetadas pela atualização. Inserir o comando commit no próprio bloco.
Exercício 12
Inserir um novo departamento 'PROJETO' com o código imediatamente superior ao do departamento RESEARCH, na cidade
de ERECHIM, através de um bloco PL-SQL. Mostrar via SQL Plus o número do novo departamento inserido e consultá-lo via
SQL Plus logo após.
Exercício 13
Queremos fazer um registro de todos os funcionários inseridos através da criação de uma trigger. Claro, você pode estar
pensando que isso poderia ser feito adicionando uma ou outra coluna na tabela EMP e criando uma consulta SQL para este
fim, mas estamos aprendendo sobre triggers e portanto faremos uma. Ela deve ser disparada sempre depois de um INSERT na
tabela EMP registrando em uma tabela auxiliar de nome EMP_INS (EMPNO, DNAME, DATA, SAL) esses dados dos
funcionário inseridos. Veja que o campo DNAME vem da tabela DEPT, e portanto o nome do departamento deve ser buscado
dessa tabela no código da trigger.
Exercício 14
Criar uma função chamada f_func_media que recebe o número de um funcionário (empno) e devolve média salarial entre
todos os funcionários do mesmo departamento do funcionário informado.
Exercício 15
Criar uma procedure que receba o nome de um departamento como parâmetro e atualize todos as linhas da tabela EMP desse
departamento que possuem funcionários com mais de 10 anos de empresa. Para isso vamos adicionar uma coluna nova na
tabela EMP chamada Func10 que, de início, terá todos os valores 'N';
update EMP
set FUNC10='N';
commit;
Pode-se alterar o fluxo lógico de instruções usando estruturas para controle de loop e instruções IF condicionais:
IF – THEN - END IF
IF – THEN – ELSE - END IF
IF – THEN – ELSIF -END IF
Instruções IF
Sintaxe
Exemplos:
2. Definir o cargo como SALESMAN, departamento 35 e comissão de 20% sobre o salário atual se o sobrenome for MILLER.
IF v_ename = 'MILLER' THEN
v_job := 'SALESMAN';
v_deptno := 35;
v_new_comm := sal * 0.20;
END IF;
3. Definir um indicador para pedidos quando houver menos de cinco dias entre a data do pedido e a data de entrega.
4. Para um determinado valor, calcular um percentual desse valor com base em uma condição.
Loops
Loop BÁSICO:
LOOP
Instrução 1;
Instrução 2;
...
EXIT (WHEN condição];
END LOOP;
Loop FOR:
Loop WHILE:
O atributo %ROWTYPE
Para declarar um registro com base em um conjunto de colunas em uma view ou tabela de banco de dados, usa-se o atributo
%rowtype.
Exemplo:
DECLARE
emp_rec emp%rowtype;
...
O registro emp_rec terá uma estrutura correspondente aos campos da tabela EMP.
- o número e tipos de dados das colunas de banco de dados subjacentes podem não ser conhecidas.
- o número e tipos de dados das colunas de banco de dados subjacentes podem ser alteradas em tempo de execução.
record_name.field_name
Exercício 16
Vamos fazer um exemplo simples para demonstrar o uso do atributo rowtype. Para isso vamos inserir através de um bloco PL-
SQL os dados do funcionário MILLER (empno = 7934), que está se aposentando, em uma tabela chamada retired_emps.
select *
from emp
where empno=7934;
DECLARE
emp_rec emp%rowtype;
BEGIN
select * into emp_rec
from emp
where empno = &numero_do_empregado;
commit;
END;
/
select *
from emp
where empno=7934;
select *
from retired_emps
where empno=7934;
Cursores explícitos
Vimos até então que cada instrução executada pelo Oracle Server tem um cursor individual associado. Podem ser implícitos,
declarados automaticamente para todas as instruções DML (recém abordados), bem como explícitos, declarados e nomeados
pelo programador. Ao usar cursores explícitos, você tem controle total sobre a iteração e processamento das linhas
retornadas pela consulta. Isso permite que você manipule as linhas conforme necessário, fazendo operações específicas em
cada uma delas.
CURSOR cursor_name IS
select_statement;
Pode-se usar também a cláusula ORDER BY se for necessária uma ordenação específica.
Exemplos:
DECLARE
CURSOR emp_cursor IS
select empno, ename
from emp;
CURSOR dept_cursor IS
select *
from dept
where deptno = 10;
Abrindo um cursor
OPEN cursor_name;
A instrução FETCH recupera as linhas no conjunto ativo uma por vez. Após cada extração, o cursor avança para a próxima
linha no conjunto ativo.
Exemplo:
...
OPEN defined_cursor;
LOOP
FETCH defined_cursor INTO defined_variables;
EXIT WHEN … ;
…
-- processamento dos dados trazidos
…
END;
Fechando o cursor
CLOSE cursor_name;
O cursor deve ser sempre fechado após o término do processamento das linhas. Pode ser reaberto se necessário. O parâmetro
OPEN_CURSORS no arquivo de inicialização do Oracle define o número máximo de cursores que podem ficar abertos para a
instância ao mesmo tempo.
Exercício 17
Recuperar os 10 primeiros funcionários que aparecem na tabela EMP.
set serverout on
DECLARE
v_empno emp.empno%TYPE;
v_ename emp.ename%TYPE;
CURSOR emp_cursor IS
select empno, ename
from emp;
BEGIN
OPEN emp_cursor;
FOR I IN 1..10 LOOP
FETCH emp_cursor INTO v_empno, v_ename;
DBMS_OUTPUT.PUT_LINE ('Funcionario ' || to_char(i) || ':' || v_empno || '-' || v_ename);
END LOOP;
CLOSE emp_cursor;
END;
/
O atributo %ISOPEN
Pode ser usado para garantir o acesso ao cursor somente quando o mesmo estiver aberto.
Exemplo:
set serverout on
DECLARE v_empno emp.empno%type;
v_ename emp.ename%type;
CURSOR emp_cursor IS
SELECT empno, ename
FROM emp;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO v_empno, v_ename;
EXIT WHEN emp_cursor%rowcount > 6 OR emp_cursor%notfound;
DBMS_OUTPUT.PUT_LINE ('Funcionario' || ': ' || v_empno || '-' || v_ename);
END LOOP;
CLOSE emp_cursor;
END;
/
Servem para processar linhas de um conjunto ativo extraindo valores para um PL-SQL Record. Pode-se também definir um
registro com base na lista selecionada de colunas em um cursor explícito. Na operação de FETCH, ao usar um registro, não é
necessário especificar cada um dos campos, já que os dados do cursor são jogados para o registro.
Exercício 18
Usar um cursor para recuperar números e nomes de funcionários e inserir em uma tabela essas informações.
DECLARE
CURSOR emp_cursor IS
SELECT empno, ename
FROM emp;
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_record;
EXIT WHEN emp_cursor%NOTFOUND;
END LOOP;
COMMIT;
CLOSE emp_cursor;
END;
/
Um loop FOR de cursor processa linhas em um cursor explícito. Ele é um atalho porque o cursor é aberto, linhas são extraídas
uma vez para cada iteração no loop e o cursor é fechado automaticamente após o processamento de todas as linhas. O loop em
si é terminado automaticamente ao final da iteração quando a última linha for extraída. É prático porque percorrerá todo o
cursor, linha por linha, permitindo processamentos em particular para cada linha, e geralmente é isso que é utilizado, já que na
maioria das vezes busca-se analisar todos os dados de um cursor e não somente conjuntos de dados dele como vimos
anteriormente quanto usamos o atributo rowcount.
Exercício 19
Mostrar os funcionários que estão trabalhando atualmente no departamento de vendas.
set serverout on
DECLARE
CURSOR emp_cursor IS
select ename, deptno
from emp;
BEGIN
FOR emp_record IN emp_cursor
LOOP
IF emp_record.deptno = 30 THEN
DBMS_OUTPUT.PUT_LINE ('O funcionario ' || emp_record.ename || ' trabalha no depto de vendas');
END IF;
END LOOP;
END;
/
Nestes casos, não é necessário declarar um cursor. Por exemplo, o exercício 9 poderia ser resolvido da seguinte forma:
set serverout on
BEGIN
FOR emp_record IN (
select ename, deptno
from emp
)
LOOP
IF emp_record.deptno = 30 THEN
DBMS_OUTPUT.PUT_LINE ('O funcionario ' || emp_record.ename || ' trabalha no depto de vendas');
END IF;
END LOOP;
END;
/
Exercício 19.A
Criar uma procedure chamada ClassificaEmp que varre a tabela de empregados usando FOR LOOP e mostra o nome do
empregado, seu salário, e uma classificação como “Salario Alto” se o salário for maior que 2.500 ou “Salario Normal” caso
contrário.
Exercício 19.B
Altere a procedure anterior para as seguintes classificações: “Salario Alto” se o salário for maior que 2.500, “Salario Medio” se
o salário for entre 1.000 e 2.500, e “Salario Baixo” se for menor que 1.000.
Exercício 19.C
Criar uma procedure chamada AumentaSalario que receba como parâmetro um valor de salário e através de um cursor com
FOR LOOP atualize todos os salários menores que o parâmetro informado aumentando os mesmos em 10%. A procedure
deverá exibir na tela o número, o nome, o salário antigo e o novo salário do funcionário.
Exercício 19.D
Mude a procedure anterior para tornar o percentual de aumento do salário um outro parâmetro passado na chamada da função.
Parâmetros permitem que valores sejam passados para um cursor quando ele é aberto e sejam usados na consulta quando ela é
executada. Cada parâmetro na declaração do cursor deve ter um parâmetro correspondente na instrução OPEN. Os tipos de
dados dos parâmetros são iguais aos das variáveis, só que não precisa ser definido um tamanho para eles.
Sintaxe:
Exemplos:
DECLARE
v_emp_job emp.job%type := 'CLERK'
v_ename emp.ename%type;
DECLARE
CURSOR emp_cursor (p_deptno NUMBER, p_job VARCHAR2) IS
SELECT …
BEGIN
FOR emp_record IN emp_cursor (10, 'ANALYST') LOOP …
É conveniente utilizar a cláusula FOR UPDATE sempre antes de alguma atualização ou exclusão de linhas. Se quiséssemos
atualizar os salários de todos os funcionários do departamento 30, poderíamos fazer um cursor que nos garantisse que as linhas
estariam liberadas para a atualização antes de disparar o comando UPDATE.
Por exemplo:
DECLARE
CURSOR emp_cursor IS
select empno, ename, sal
from emp
where deptno = 30
FOR UPDATE OF sal NOWAIT;
Uma subconsulta é uma consulta, geralmente entre parênteses, que aparece dentro de outra instrução DML. São geralmente
usadas na cláusula WHERE de uma sentença SQL, mas podem ser usadas também na cláusula FROM.
Exemplo:
DECLARE
CURSOR funcs_cursor IS
select t1.deptno, t1.dname, t2.total
from dept t1, ( select deptno, count(*) total
from emp
group by deptno ) t2
where t1.deptno = t2.deptno and
t2.total > 3;
No exemplo anterior, o cursor possui uma subconsulta na cláusula FROM, que monta uma tabela temporária contendo o
número de funcionários por departamento. Assim, ele pode retornar somente o código e o nome dos departamentos que
possuam mais de 3 funcionários, bem como o total de funcionários desses departamentos.
Exercício 20
Usar um cursor para recuperar o número e o nome dos departamentos a partir da tabela DEPT. Passar o número do
departamento para outro cursor que deverá recuperar os detalhes sobre o nome do funcionário, cargo, data de admissão e
salário de todos os funcionários que trabalham naquele departamento a partir da tabela EMP.
Tratando exceções
Uma exceção é um identificador em PL-SQL criado durante a execução de um bloco que termina seu corpo principal de ações.
Um bloco sempre termina quando o código PL-SQL cria uma exceção, mas pode-se especificar um handler (manipulador) de
exceções para executar ações finais.
quando o próprio Oracle Server gera a mesma. Por exemplo, na ocorrência do erro ORA-01403, quando nenhuma
linha é recuperada do banco de dados em uma instrução SELECT. Nesse momento, será gerada a exceção
NO_DATA_FOUND
quando a exceção é criada explicitamente, emitindo a instrução RAISE dentro do bloco. Trata-se de uma exceção
definida pelo programador
Uma exceção pode ser capturada pelo manipulador e tratada dentro do bloco, concluindo o mesmo com êxito, ou se propagar
para o ambiente que chamou o procedimento PL-SQL terminando o processo com falha.
Sintaxe
EXCEPTION
WHEN exceção1 [ OR exceção2 . . . ] THEN
instrução1;
instrução2;
...
[ WHEN exceção3 [ OR exceção4 . . . ] THEN
instrução1;
instrução2;
...]
[ WHEN OTHERS THEN
instrução1;
instrução2;
...]
O handler de exceção WHEN OTHERS captura qualquer exceção não tratada. Por isso é o último handler de exceção definido.
Erro do Oracle
Nome da exceção Quando ocorre
Server
CURSOR_ALREADY_OPEN ORA-06511 Na tentativa de abertura de um cursor já aberto
INVALID_NUMBER ORA-01722 Na falha de conversão de string de caracteres para número
LOGIN_DENIED ORA-01017 Ao estabelecer login com o Oracle Server com usuário ou senha inválido(a)
NO_DATA_FOUND ORA-01403 Em consultas de linha única que não retornam dados
NOT_LOGGED_ON ORA-01012 Em uma chamada ao banco de dados sem estar conectado ao mesmo
TIMEOUT_ON_RESOURCE ORA-00051 Ao exceder o tempo limite de espera por um recurso
TOO_MANY_ROWS ORA-01422 Em consultas de linha única que retornam mais de uma linha
ZERO_DIVIDE ORA-01476 Na tentativa de divisão por ZERO
Exemplo
BEGIN
EXCEPTION
when NO_DATA_FOUND then
instrucao1;
instrucao2;
when TOO_MANY_ROWS then
instrucao1;
when OTHERS then
instrucao1;
instrucao2;
instrucao3;
END;
Exercício 21
No bloco PLSQL abaixo estamos buscando na tabela emp o nome de um empregado por cargo e jogando para dentro de duas
variáveis a serem usadas na função PUT_LINE. Porém, sempre é esperado que tal SQL somente retorne uma linha, e, assim,
zero linhas ou mais de uma linha seria um erro, que será tratado pelas exceções inseridas. Vamos entrar primeiro com o valor
PRESIDENT para o filtro da cláusula WHERE. Tudo funciona bem porque existe só 1 presidente. Mudar depois o job para
SALESMAN para gerar a exception TOO_MANY_ROWS. Depois trocar o job para DBA para gerar a exception
NO_DATA_FOUND.
set serverout on
DECLARE
v_ename emp.ename%type;
v_deptno emp.deptno%type;
BEGIN
EXCEPTION
END;
/
Exercício 22
Mas e o que aconteceria com o mesmo bloco PLSQL se as exceções não fossem inseridas. Veja abaixo que o bloco é idêntico
ao anterior porém sem o tratamento de exceções. Insira PRESIDENT, SALESMAN e DBA novamente observando o que
acontece em cada um dos casos.
set serverout on
DECLARE
v_ename emp.ename%type;
v_deptno emp.deptno%type;
BEGIN
END;
/
Qual diferença você percebeu entre as execuções dos exercícios anteriores, ou seja, quando havia tratamento de exceções e
quando não havia?
_________________________________________________________________________________________________
Com a prática você perceberia que quando o SGBD gera um erro, o bloco PLSQL, esteja ele em um script, dentro de uma
procedure, de uma trigger ou de uma função, é abortado. No entanto, se há tratamento de exceções, o processamento poderá
continuar conforme programado.
Exercício 22.A
Criar um bloco PL/SQL que mostra quantas vezes o maior salário da empresa é maior do que o salário de cada funcionário,
mostrando também o nome de cada funcionário.
Os nomes dos funcionários devem aparecer em ordem decrescente de seus números.
Exercício 22.B
Inserir agora o seguinte funcionário em EMP:
insert into emp (empno, ename, job, mgr, hiredate, sal, deptno) values (8000,'MATHEUS', 'CLERK', 7935, sysdate, 0, 10);
commit;
Exercício 22.C
Inserir agora no bloco PL/SQL uma exceção do tipo ZERO_DIVIDE que resolva o problema.
Exercício 22.D
Mudar agora a exceção de ZERO_DIVIDE para OTHERS mas mostrando na mensagem o código de erro do Oracle.