Aula 17: PL/SQL básico — blocos anônimos, variáveis e controle de fluxo

Aula 17: PL/SQL básico — blocos anônimos, variáveis e controle de fluxo

Bem-vindo à décima sétima aula do curso Oracle SQL — Do Zero ao Avançado. Se você acompanhou as aulas anteriores, já domina consultas complexas, junções, subqueries, funções analíticas e operações DML. Agora, chegou o momento de cruzar a fronteira entre o SQL puro e o mundo da programação dentro do banco de dados. Hoje vamos mergulhar no PL/SQL básico, a linguagem procedural da Oracle que transforma o SGBD em uma verdadeira plataforma de desenvolvimento. Esta é, sem dúvida, uma das aulas mais transformadoras do curso — depois dela, sua forma de pensar sobre o Oracle Database mudará completamente.

O PL/SQL básico é o fundamento sobre o qual se constroem procedures, functions, packages e triggers. Compreender blocos anônimos, declaração de variáveis e estruturas de controle de fluxo é o primeiro passo para automatizar tarefas que, em SQL puro, seriam impossíveis ou extremamente trabalhosas. Em nossos projetos na JRT Technology Solutions, utilizamos diariamente blocos PL/SQL para validação de dados, migrações, rotinas de manutenção e integração entre sistemas — e é exatamente essa capacidade que você começará a adquirir hoje.

Nesta aula, você construirá seus primeiros blocos anônimos do zero. Vamos começar com a anatomia de um bloco PL/SQL, avançar pela declaração de variáveis de diferentes tipos, explorar comandos condicionais (IF, CASE) e dominar as estruturas de repetição (LOOP, WHILE, FOR). Tudo isso com exemplos 100% práticos e executáveis no seu ambiente Oracle, seja ele o SQL*Plus, o SQL Developer ou qualquer cliente compatível. Ao final, você terá condições reais de escrever scripts PL/SQL para resolver problemas do dia a dia de um DBA ou desenvolvedor Oracle.

Prepare seu ambiente: você precisará de uma instância Oracle funcional (19c ou superior recomendável, mas os conceitos se aplicam desde o 11g) e acesso a um schema com privilégios básicos de CREATE SESSION, CREATE TABLE e INSERT. Se você realizou os laboratórios das aulas anteriores, já tem a base HR ou um schema próprio para testes. Vamos codificar juntos — abra seu cliente SQL e venha dominar o PL/SQL básico.

O que você vai aprender nesta aula

  • A anatomia completa de um bloco anônimo PL/SQL e suas seções obrigatórias e opcionais
  • Como declarar e inicializar variáveis escalares, compostas e reference types
  • O uso correto do comando SELECT INTO para popular variáveis a partir de tabelas
  • Controle de fluxo condicional com IF/ELSIF/ELSE e CASE
  • Estruturas de repetição: LOOP simples, WHILE e FOR
  • Introdução ao tratamento de exceções com EXCEPTION
  • Como depurar blocos utilizando DBMS_OUTPUT.PUT_LINE
  • Erros frequentes de iniciantes em PL/SQL básico e como corrigi-los rapidamente

Pré-requisitos e Ambiente

Para executar todos os exemplos desta aula sem interrupções, você precisa ter uma instância Oracle Database em funcionamento. Pode ser uma instalação local (Windows, Linux), um container Docker com a imagem oficial container-registry.oracle.com/database/express:21.3.0, ou até mesmo uma instância no Oracle Cloud (Always Free Tier). Os exemplos foram testados no Oracle Database 19c Enterprise Edition e 21c Express Edition, ambos compatíveis com o conteúdo apresentado.

Confirme que o parâmetro SERVEROUTPUT está habilitado ou prepare-se para ativá-lo no início de cada sessão. Esse parâmetro controla a exibição das mensagens enviadas pelo pacote DBMS_OUTPUT, que usaremos extensivamente para depuração. Se você estiver no SQL*Plus, execute SET SERVEROUTPUT ON; no SQL Developer, ative a aba DBMS Output clicando no ícone verde de “+” e selecionando sua conexão. Este passo é essencial — sem ele, você não verá os resultados dos exemplos e poderá pensar erroneamente que o código não funcionou.

Recomendamos também criar uma tabela simples para os exercícios de SELECT INTO e loops. Se você já possui o schema HR desbloqueado, ótimo. Caso contrário, execute o script abaixo para criar uma tabela de exemplo chamada FUNCIONARIOS:

-- Conecte-se como seu usuário de prática (ex: STUDENT, DEV, etc.)
CREATE TABLE funcionarios (
    id        NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    nome      VARCHAR2(100) NOT NULL,
    cargo     VARCHAR2(50),
    salario   NUMBER(10,2),
    data_adm  DATE DEFAULT SYSDATE
);

-- Inserindo dados de exemplo
INSERT INTO funcionarios (nome, cargo, salario) VALUES ('Ana Silva', 'Analista', 5200.00);
INSERT INTO funcionarios (nome, cargo, salario) VALUES ('Bruno Costa', 'Desenvolvedor', 4800.50);
INSERT INTO funcionarios (nome, cargo, salario) VALUES ('Carla Mendes', 'DBA', 7500.00);
INSERT INTO funcionarios (nome, cargo, salario) VALUES ('Diego Alves', 'Estagiário', 1800.00);
INSERT INTO funcionarios (nome, cargo, salario) VALUES ('Elena Ferreira', 'Gerente', 9200.00);
COMMIT;
Tabela criada.

1 linha inserida.
1 linha inserida.
1 linha inserida.
1 linha inserida.
1 linha inserida.

Commit concluído.

Com o ambiente preparado e a tabela populada, estamos prontos para iniciar nossa jornada no PL/SQL básico. Lembre-se: cada bloco de código mostrado foi projetado para ser executado exatamente como está. Copie, cole e execute — a verificação imediata da saída é o segredo para fixar o aprendizado.

O que é PL/SQL e como ele se diferencia do SQL puro

PL/SQL (Procedural Language/Structured Query Language) é a extensão procedural da Oracle para o SQL padrão. Enquanto o SQL é uma linguagem declarativa — você descreve o que quer, e o otimizador decide como obter —, o PL/SQL adiciona lógica imperativa: variáveis, condicionais, loops, tratamento de exceções e modularização. Isso significa que você pode escrever programas completos que residem dentro do banco de dados, próximos dos dados, reduzindo tráfego de rede e aumentando performance. Na JRT Technology Solutions, nossos especialistas utilizam PL/SQL diariamente para construir APIs de banco, rotinas ETL e processos batch que processam milhões de registros com eficiência.

A unidade fundamental do PL/SQL básico é o bloco. Um bloco pode ser anônimo (executado uma única vez, sem nome) ou nomeado (procedures, functions, packages, triggers — que veremos nas aulas seguintes). O bloco anônimo é o ponto de partida ideal porque permite testar lógica sem criar objetos permanentes no banco. Ele segue uma estrutura rígida de três seções: DECLARE (opcional, para variáveis), BEGIN (obrigatória, para comandos executáveis) e EXCEPTION (opcional, para tratamento de erros), finalizando com END;.

Uma diferença crucial que muitos iniciantes demoram a entender é que o PL/SQL não retorna resultados de consultas diretamente como o SQL. Para visualizar dados dentro de um bloco, você precisa explicitamente capturá-los em variáveis (com SELECT INTO) e depois exibi-los (com DBMS_OUTPUT) ou inseri-los em tabelas. Esta mudança de paradigma — de “result set” para “processamento linha a linha” — é o que confere ao PL/SQL seu poder de automação.

Outro ponto fundamental: o PL/SQL é fortemente integrado ao SQL. Você pode embutir qualquer comando DML (INSERT, UPDATE, DELETE, MERGE) e consultas SELECT dentro dos blocos, utilizando variáveis para amarrar parâmetros. Essa simbiose elimina a necessidade de “context switching” excessivo entre aplicação e banco de dados, um dos principais gargalos de performance em sistemas corporativos. Ao final desta aula, você compreenderá perfeitamente por que o PL/SQL básico é considerado uma habilidade indispensável para qualquer profissional Oracle.

Anatomia do Bloco Anônimo no PL/SQL básico

Todo bloco PL/SQL, por mais complexo que seja, obedece à mesma estrutura fundamental. Compreender cada seção é essencial antes de avançarmos para tópicos mais elaborados. Vamos dissecar o esqueleto de um bloco anônimo:

-- Estrutura mínima de um bloco anônimo PL/SQL
DECLARE
    -- [OPCIONAL] Declaração de variáveis, constantes, cursores, tipos
    v_mensagem VARCHAR2(100);
BEGIN
    -- [OBRIGATÓRIO] Comandos executáveis (SQL, laços, condicionais)
    v_mensagem := 'Meu primeiro bloco PL/SQL!';
    DBMS_OUTPUT.PUT_LINE(v_mensagem);
EXCEPTION
    -- [OPCIONAL] Captura e tratamento de exceções
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Erro: ' || SQLERRM);
END;
/
Meu primeiro bloco PL/SQL!

Procedimento PL/SQL concluído com sucesso.

Vamos analisar cada elemento. A palavra-chave DECLARE inicia a zona de declarações — aqui você define todas as variáveis e objetos que serão utilizados no bloco. Se o bloco não precisar de variáveis, esta seção pode ser completamente omitida (blocos “BEGIN…END;” direto). A seção BEGIN é o coração do bloco: contém as instruções que serão executadas sequencialmente. Note que em PL/SQL, a atribuição de valores a variáveis utiliza o operador := (dois-pontos seguido de igual), não o = simples (que é reservado para comparações).

A seção EXCEPTION é um dos recursos mais poderosos do PL/SQL básico. Ela permite capturar erros que ocorrem durante a execução e tratá-los de forma elegante, sem quebrar a aplicação. O handler WHEN OTHERS THEN captura qualquer exceção não tratada explicitamente — veremos mais sobre exceções específicas adiante. Finalmente, o bloco é encerrado com END; e, no SQL*Plus, a execução é disparada pela barra (/) na linha seguinte. Esta barra é específica do SQL*Plus e do SQL Developer (em modo script); se você estiver usando a interface gráfica do SQL Developer e executando com F5 ou Ctrl+Enter, ela pode ser omitida. No entanto, recomendamos sempre incluí-la para compatibilidade.

Um detalhe que confunde muitos alunos: o PL/SQL permite aninhamento de blocos. Dentro da seção BEGIN de um bloco externo, você pode declarar um novo DECLARE...BEGIN...END interno, criando escopos léxicos onde variáveis do bloco interno não são visíveis ao bloco externo e vice-versa (a menos que referenciadas explicitamente). Esta técnica é amplamente utilizada em nossos projetos na JRT para isolar lógicas complexas e evitar poluição do namespace. No PL/SQL básico, no entanto, focaremos em blocos simples e lineares.

Declaração de Variáveis e Tipos no PL/SQL básico

Variáveis são contêineres nomeados que armazenam valores manipuláveis durante a execução do bloco. No PL/SQL básico, a declaração segue o formato nome_variavel [CONSTANT] tipo [NOT NULL] [:= valor_inicial];. A Oracle oferece uma ampla gama de tipos de dados, muitos deles equivalentes aos tipos SQL que você já conhece, mas com extensões específicas para programação. A tabela abaixo resume os tipos mais utilizados no dia a dia:

Tipo PL/SQL Descrição Exemplo de Declaração
VARCHAR2(n) String de comprimento variável, máximo n bytes (até 32767 em PL/SQL) v_nome VARCHAR2(100) := 'Oracle';
NUMBER(p,s) Numérico com precisão p e escala s v_salario NUMBER(10,2) := 5000.00;
DATE Data e hora (até segundos) v_hoje DATE := SYSDATE;
BOOLEAN Lógico: TRUE, FALSE ou NULL (exclusivo PL/SQL, não existe em SQL) v_flag BOOLEAN := TRUE;
INTEGER Sub-tipo de NUMBER para inteiros v_cont INTEGER := 0;
tabela.coluna%TYPE “Anchored type”: herda o tipo da coluna da tabela v_sal func.salario%TYPE;
tabela%ROWTYPE Registro completo com a estrutura de uma linha da tabela v_reg func%ROWTYPE;

O uso de %TYPE e %ROWTYPE é uma das melhores práticas mais recomendadas no PL/SQL básico. Ao ancorar uma variável a uma coluna de tabela, você blinda seu código contra alterações estruturais futuras — se a coluna SALARIO mudar de NUMBER(10,2) para NUMBER(12,4), sua variável automaticamente acompanha a mudança, sem necessidade de refatoração. Essa é uma técnica que nossos arquitetos da JRT Technology Solutions exigem em todas as entregas de código PL/SQL.

Constantes são declaradas com a palavra-chave CONSTANT e devem ser inicializadas obrigatoriamente na declaração. Uma vez atribuído, seu valor não pode ser alterado durante toda a execução do bloco. Exemplo típico: v_taxa_imposto CONSTANT NUMBER(5,2) := 0.15;. Tentativas de reatribuição geram o erro PLS-00363. Já variáveis com a constraint NOT NULL também exigem inicialização na declaração e não podem receber NULL posteriormente — uma salvaguarda valiosa para lógica de negócio.

Veja um bloco completo que demonstra diferentes tipos de variáveis e o uso de %TYPE e %ROWTYPE:

DECLARE
    v_id        funcionarios.id%TYPE;
    v_nome      funcionarios.nome%TYPE;
    v_salario   funcionarios.salario%TYPE;
    v_reg       funcionarios%ROWTYPE;         -- Registro completo
    v_msg       VARCHAR2(200);
    v_bonus     CONSTANT NUMBER(5,2) := 0.10; -- 10% fixo
BEGIN
    -- SELECT INTO para popular variáveis escalares
    SELECT id, nome, salario
    INTO   v_id, v_nome, v_salario
    FROM   funcionarios
    WHERE  id = 1;

    v_msg := 'Funcionário ' || v_nome || ' (ID:' || v_id || ') ganha R$ ' || v_salario;
    DBMS_OUTPUT.PUT_LINE(v_msg);
    DBMS_OUTPUT.PUT_LINE('Bônus previsto: R$ ' || ROUND(v_salario * v_bonus, 2));

    -- SELECT INTO para popular um ROWTYPE inteiro
    SELECT * INTO v_reg FROM funcionarios WHERE id = 3;
    DBMS_OUTPUT.PUT_LINE('Registro completo: ' || v_reg.nome || ' - ' || v_reg.cargo);
END;
/
Funcionário Ana Silva (ID:1) ganha R$ 5200
Bônus previsto: R$ 520
Registro completo: Carla Mendes - DBA

Procedimento PL/SQL concluído com sucesso.

Observe que no segundo SELECT INTO utilizamos o curinga * para preencher todas as colunas do registro v_reg de uma só vez. O acesso aos campos individuais é feito com a notação de ponto (v_reg.nome). Essa técnica é extremamente produtiva em rotinas que manipulam linhas completas, como migrações e validações.

Controle de Fluxo Condicional: IF, ELSIF e ELSE no PL/SQL básico

O comando IF é a estrutura condicional mais básica e versátil do PL/SQL básico. Sua sintaxe é intuitiva para quem já programa em qualquer linguagem, mas possui particularidades importantes. O formato completo é: IF condicao1 THEN ... ELSIF condicao2 THEN ... ELSE ... END IF;. Note que o ELSIF do Oracle é uma palavra só (sem o segundo “E”), e que todo bloco IF deve ser finalizado com END IF; (com espaço e ponto-e-vírgula). Esquecer o END IF é um dos erros de sintaxe mais comuns entre iniciantes.

As condições podem usar todos os operadores relacionais padrão (=, <, >, <=, >=, <> ou !=) e operadores lógicos (AND, OR, NOT). Uma peculiaridade importante: valores NULL em condições produzem resultados desconhecidos (UNKNOWN) e não são tratados como FALSE. Por isso, sempre utilize IS NULL ou IS NOT NULL explicitamente, ou funções como NVL() para evitar comportamentos inesperados.

Vamos construir um exemplo que classifica funcionários por faixa salarial utilizando IF/ELSIF/ELSE:

DECLARE
    v_nome      funcionarios.nome%TYPE;
    v_salario   funcionarios.salario%TYPE;
    v_faixa     VARCHAR2(20);
BEGIN
    SELECT nome, salario
    INTO   v_nome, v_salario
    FROM   funcionarios
    WHERE  id = 2;  -- Bruno Costa, salário 4800.50

    IF v_salario > 7000 THEN
        v_faixa := 'Premium';
    ELSIF v_salario >= 4000 THEN
        v_faixa := 'Pleno';
    ELSIF v_salario >= 2000 THEN
        v_faixa := 'Júnior';
    ELSE
        v_faixa := 'Estagiário';
    END IF;

    DBMS_OUTPUT.PUT_LINE(v_nome || ' está na faixa: ' || v_faixa);
END;
/
Bruno Costa está na faixa: Pleno

Procedimento PL/SQL concluído com sucesso.

Perceba que as condições são avaliadas em ordem — a primeira que retornar TRUE dispara o bloco correspondente, e as demais são ignoradas. Isso torna a ordem dos ELSIF relevante: se a condição >= 4000 viesse antes de > 7000, valores acima de 7000 cairiam na faixa "Pleno", o que seria incorreto. Em lógica de classificação, sempre ordene os testes do mais restritivo para o menos restritivo. Esta é uma prática diária dos analistas da JRT Technology Solutions em rotinas de categorização de clientes, incidentes e ativos.

Expressão CASE no PL/SQL básico: alternativa elegante ao IF

Quando você precisa testar múltiplos valores de uma mesma variável, a expressão CASE oferece uma sintaxe mais limpa e legível que longas cadeias de IF/ELSIF. O PL/SQL suporta duas formas de CASE: o CASE simples (compara uma expressão contra valores fixos) e o CASE searched (avalia condições booleanas, similar ao IF). Ambos são encerrados com END CASE;.

O CASE como expressão também pode retornar valores diretamente para variáveis, eliminando a necessidade de múltiplas atribuições. Veja o mesmo exemplo de faixa salarial reescrito com CASE searched:

DECLARE
    v_nome      funcionarios.nome%TYPE;
    v_salario   funcionarios.salario%TYPE;
    v_faixa     VARCHAR2(20);
BEGIN
    SELECT nome, salario INTO v_nome, v_salario FROM funcionarios WHERE id = 4; -- Diego

    v_faixa := CASE
                   WHEN v_salario > 7000 THEN 'Premium'
                   WHEN v_salario >= 4000 THEN 'Pleno'
                   WHEN v_salario >= 2000 THEN 'Júnior'
                   ELSE 'Estagiário'
               END;

    DBMS_OUTPUT.PUT_LINE(v_nome || ' está na faixa: ' || v_faixa);
END;
/
Diego Alves está na faixa: Estagiário

Procedimento PL/SQL concluído com sucesso.

Note que a atribuição v_faixa := CASE ... END; em uma única linha é possível porque o CASE é uma expressão que retorna valor. Isso torna o código mais conciso e fácil de manter. O CASE simples, por outro lado, é útil quando você testa uma única variável contra vários valores discretos — por exemplo, mapear códigos de erro para mensagens amigáveis.

Abaixo, uma tabela comparativa para ajudar você a decidir quando usar cada estrutura:

Critério IF / ELSIF / ELSE CASE (expressão) CASE (comando)
Complexidade das condições Alta — permite qualquer expressão booleana Média — condições nos WHEN Média — condições nos WHEN
Retorna valor para variável Não diretamente (precisa de atribuição interna) Sim, como expressão Não (controle de fluxo puro)
Legibilidade com muitos casos Baixa (muitos ELSIF aninhados) Alta Alta
Uso típico Lógica de negócio complexa com múltiplas variáveis Mapeamento de valores, decodificação Execução condicional de blocos de comando

Em nossos projetos na JRT, tendemos a preferir CASE para mapeamentos e decodificações, e IF para lógica de decisão que envolve múltiplas variáveis ou efeitos colaterais (como execuções de DML condicionais). Não existe uma regra absoluta — a legibilidade e a manutenibilidade do código devem ser o guia principal.

Estruturas de Repetição no PL/SQL básico: LOOP, WHILE e FOR

As estruturas de repetição (ou laços) são o que realmente diferencia o PL/SQL básico do SQL declarativo. Elas permitem processar conjuntos de dados linha a linha, executar operações repetitivas até que uma condição seja satisfeita e iterar sobre ranges numéricos. O Oracle PL/SQL oferece três construções principais: LOOP simples (infinito, requer EXIT explícito), WHILE...LOOP (pré-condição) e FOR...LOOP (iteração com contador).

O LOOP simples é a forma mais básica. Ele executa indefinidamente até encontrar um comando EXIT ou EXIT WHEN. É ideal para situações onde a condição de saída só pode ser avaliada no meio do processamento (ex: leitura de arquivos, polling de status). Sem um EXIT, o bloco executará para sempre — em ambiente Oracle, isso geralmente resulta em timeout ou estouro de recursos, não em travamento total (o DBA pode matar a sessão).

O WHILE...LOOP avalia a condição antes de cada iteração. Se a condição for falsa já na primeira verificação, o corpo do laço não é executado nenhuma vez. É a escolha certa quando o número de iterações é desconhecido mas a condição de continuação é clara.

O FOR...LOOP é o mais utilizado no dia a dia. Ele itera sobre um range numérico (FOR i IN 1..10) ou sobre os resultados de um cursor (FOR registro IN (SELECT ...)). A variável do contador é implícita — você não precisa declará-la no DECLARE, e ela é acessível somente dentro do loop. Vejam um exemplo abrangente que demonstra os três tipos:

DECLARE
    v_contador  INTEGER := 0;
    v_soma      NUMBER := 0;
BEGIN
    DBMS_OUTPUT.PUT_LINE('=== LOOP simples com EXIT WHEN ===');
    LOOP
        v_contador := v_contador + 1;
        DBMS_OUTPUT.PUT_LINE('Iteração: ' || v_contador);
        EXIT WHEN v_contador >= 3;  -- Sai quando atinge 3
    END LOOP;

    DBMS_OUTPUT.PUT_LINE('=== WHILE LOOP ===');
    v_contador := 0;  -- Reset
    WHILE v_contador < 3 LOOP
        v_contador := v_contador + 1;
        DBMS_OUTPUT.PUT_LINE('While iteração: ' || v_contador);
    END LOOP;

    DBMS_OUTPUT.PUT_LINE('=== FOR LOOP com range numérico ===');
    FOR i IN 1..3 LOOP
        v_soma := v_soma + i;
        DBMS_OUTPUT.PUT_LINE('i=' || i || ', soma parcial=' || v_soma);
    END LOOP;

    DBMS_OUTPUT.PUT_LINE('=== FOR LOOP sobre consulta (cursor implícito) ===');
    FOR rec IN (SELECT nome, salario FROM funcionarios WHERE salario > 4000 ORDER BY salario) LOOP
        DBMS_OUTPUT.PUT_LINE(rec.nome || ' ganha R$ ' || rec.salario);
    END LOOP;
END;
/
=== LOOP simples com EXIT WHEN ===
Iteração: 1
Iteração: 2
Iteração: 3
=== WHILE LOOP ===
While iteração: 1
While iteração: 2
While iteração: 3
=== FOR LOOP com range numérico ===
i=1, soma parcial=1
i=2, soma parcial=3
i=3, soma parcial=6
=== FOR LOOP sobre consulta (cursor implícito) ===
Ana Silva ganha R$ 5200
Carla Mendes ganha R$ 7500
Elena Ferreira ganha R$ 9200

Procedimento PL/SQL concluído com sucesso.

O FOR LOOP sobre cursor implícito merece destaque especial. Ele abre, percorre e fecha o cursor automaticamente — você não precisa se preocupar com OPEN, FETCH ou CLOSE. A cada iteração, a variável rec (que você nomeia como quiser) contém uma linha do resultado da consulta, e seus campos são acessados com a notação de ponto. Este é, de longe, o padrão mais comum em scripts PL/SQL nas empresas que a JRT Technology Solutions atende, sendo utilizado para processamento batch de dados, migrações e relatórios.

Um cuidado importante: dentro de loops que contêm DML (INSERT, UPDATE, DELETE), é mandatório gerenciar transações com COMMIT periódicos para evitar esgotamento do undo tablespace. A cada 10.000 ou 50.000 registros (dependendo do volume), insira um COMMIT e, se necessário, mantenha um contador para retomar de onde parou em caso de falha. Essa técnica de "commit em lote" é padrão em qualquer rotina de produção robusta.

Tratamento de Exceções no PL/SQL básico

Nenhum programa profissional está completo sem tratamento adequado de erros, e o PL/SQL básico oferece um sistema de exceções maduro e flexível. As exceções em Oracle se dividem em duas categorias: predefinidas (como NO_DATA_FOUND, TOO_MANY_ROWS, ZERO_DIVIDE) e definidas pelo usuário (declaradas com nome e código de erro, ou geradas com RAISE_APPLICATION_ERROR). A seção EXCEPTION captura os erros que ocorrem no bloco BEGIN e permite uma saída controlada.

A exceção mais encontrada por iniciantes é a NO_DATA_FOUND (ORA-01403), que ocorre quando um SELECT INTO não retorna nenhuma linha. Diferente do SQL puro — onde uma consulta vazia simplesmente retorna "zero linhas" sem erro —, o SELECT INTO em PL/SQL exige que exatamente uma linha seja retornada. Zero linhas causa NO_DATA_FOUND; mais de uma linha causa TOO_MANY_ROWS. Por isso, sempre envolva SELECT INTO em blocos com tratamento de exceção apropriado ou utilize cursores quando múltiplas linhas forem esperadas.

Veja um exemplo robusto que captura ambas as exceções:

DECLARE
    v_nome funcionarios.nome%TYPE;
    v_id   NUMBER := &id_consulta;  -- & solicita input no SQL*Plus / SQL Developer
BEGIN
    SELECT nome INTO v_nome FROM funcionarios WHERE id = v_id;
    DBMS_OUTPUT.PUT_LINE('Funcionário encontrado: ' || v_nome);
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Erro: Nenhum funcionário com ID ' || v_id);
    WHEN TOO_MANY_ROWS THEN
        DBMS_OUTPUT.PUT_LINE('Erro: Mais de um funcionário retornado (estranho para ID único)');
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Erro inesperado: ' || SQLERRM);
        DBMS_OUTPUT.PUT_LINE('Código Oracle: ' || SQLCODE);
END;
/
-- Supondo que o usuário digitou 99 (ID inexistente):
Erro: Nenhum funcionário com ID 99

Procedimento PL/SQL concluído com sucesso.

A função SQLERRM retorna a mensagem de erro associada à exceção capturada, e SQLCODE retorna o código numérico (por exemplo, +100 para NO_DATA_FOUND, -1403 se não capturada). Em projetos da JRT, utilizamos exceções personalizadas disparadas via RAISE_APPLICATION_ERROR(-20001, 'Mensagem customizada') para validar regras de negócio diretamente no banco, evitando que dados inconsistentes cheguem à aplicação. Este tópico será aprofundado na aula sobre procedures e functions.

Jamais deixe um bloco de produção com WHEN OTHERS THEN NULL; (o famoso "engolir exceção"), a menos que haja uma razão extremamente bem documentada. Exceções silenciadas são a causa raiz de incontáveis bugs difíceis de rastrear. No mínimo, registre o erro em uma tabela de log — prática que já salvou inúmeros sistemas administrados por nossos especialistas.

Verificando a Instalação / Testando a Configuração

Nesta seção, você executará comandos de verificação para garantir que seu ambiente está corretamente configurado para o PL/SQL básico. Esses testes não apenas validam a conectividade, mas também demonstram que o recurso DBMS_OUTPUT está funcional — essencial para acompanhar os exemplos da aula.

Execute os comandos abaixo exatamente na ordem apresentada. Se qualquer saída divergir significativamente, verifique os pré-requisitos no início da aula antes de prosseguir.

-- Teste 1: Verificar versão do Oracle e conectividade
SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

1 linha selecionada.
-- Teste 2: Habilitar saída do DBMS_OUTPUT e executar bloco mínimo
SET SERVEROUTPUT ON;

BEGIN
    DBMS_OUTPUT.PUT_LINE('Ambiente PL/SQL operacional em ' || TO_CHAR(SYSDATE, 'DD/MM/YYYY HH24:MI:SS'));
END;
/
Ambiente PL/SQL operacional em 18/07/2026 14:35:22

Procedimento PL/SQL concluído com sucesso.
-- Teste 3: Verificar existência da tabela de apoio
SELECT COUNT(*) AS total_registros FROM funcionarios;
TOTAL_REGISTROS
--------------
             5

1 linha selecionada.
-- Teste 4: Executar bloco com SELECT INTO para validar permissões
DECLARE
    v_teste VARCHAR2(50);
BEGIN
    SELECT nome INTO v_teste FROM funcionarios WHERE id = 1;
    DBMS_OUTPUT.PUT_LINE('Teste SELECT INTO OK: ' || v_teste);
END;
/
Teste SELECT INTO OK: Ana Silva

Procedimento PL/SQL concluído com sucesso.

Se todos os quatro testes retornarem saídas similares às mostradas acima, seu ambiente está perfeitamente configurado para acompanhar esta aula de PL/SQL básico. Caso enfrente problemas, revise a seção de pré-requisitos e a tabela de erros comuns abaixo.

Erros Comuns e Como Resolver

Durante a jornada de aprendizado do PL/SQL básico, alguns tropeços são praticamente universais. Compilamos aqui os erros mais frequentes que nossos instrutores da JRT Technology Solutions observam nos treinamentos, junto com diagnósticos precisos e soluções imediatas.

  • Erro: PLS-00201: identifier '...' must be declared
    Causa: Você referenciou uma variável que não foi declarada na seção DECLARE, ou cometeu um erro de digitação no nome.
    Sintoma: O bloco nem chega a executar; o erro é lançado em tempo de compilação.
    Solução: Verifique a declaração de todas as variáveis usadas no bloco. Lembre-se que PL/SQL é case-insensitive para identificadores, mas erros de ortografia são a causa mais comum. Se você declarou v_contador e está tentando usar v_cont, o erro aparecerá. Use um editor com autocomplete (como o SQL Developer) para minimizar esse problema.
  • Erro: ORA-01403: no data found no SELECT INTO
    Causa: O SELECT INTO não encontrou nenhuma linha que satisfizesse a condição WHERE.
    Sintoma: O bloco compila mas aborta na linha do SELECT INTO com exceção não tratada.
    Solução: Sempre envolva SELECT INTO em um bloco BEGIN...EXCEPTION...END interno (bloco aninhado) para capturar NO_DATA_FOUND, ou utilize um cursor com FETCH que permite verificar %NOTFOUND. Alternativamente, funções agregadas como MAX() ou MIN() garantem retorno (NULL se não houver dados), eliminando o risco de NO_DATA_FOUND.
  • Erro: PLS-00103: Encountered the symbol "..." when expecting one of the following
    Causa: Erro de sintaxe — geralmente falta de ponto-e-vírgula, END IF não fechado, ou barra (/) em local errado.
    Sintoma: O erro aponta para uma linha próxima ao problema real, mas nem sempre exatamente na linha certa.
    Solução: Revise a estrutura do bloco: cada comando dentro do BEGIN termina com ;; cada IF tem seu END IF; correspondente; cada LOOP tem seu END LOOP;. Use indentação consistente para facilitar a visualização de blocos não fechados.
  • Erro: ORA-06502: PL/SQL: numeric or value error: character string buffer too small
    Causa: Você declarou uma variável VARCHAR2 com tamanho insuficiente para o valor que está tentando armazenar.
    Sintoma: O erro ocorre em tempo de execução, na linha de atribuição ou concatenação.
    Solução: Aumente o tamanho máximo da variável. Considere usar VARCHAR2(4000) para strings de propósito geral, ou CLOB para textos muito longos. Lembre-se que em PL/SQL, diferentemente do SQL, VARCHAR2 pode ter até 32767 bytes — use essa capacidade a seu favor.
  • Erro: SP2-0042: unknown command "END" - rest of line ignored (SQL*Plus)
    Causa: Falta de ponto-e-vírgula antes do END;, ou execução linha a linha sem estar em modo bloco.
    Sintoma: O bloco não é reconhecido como PL/SQL e o SQL*Plus tenta interpretar cada linha como comando autônomo.
    Solução: Sempre garanta que o END; seja precedido por um ponto-e-vírgula nos comandos anteriores. Digite o bloco completo e finalize com a barra (/) em uma nova linha. No SQL Developer, execute com F9 (Run Statement) ou F5 (Run Script).

Boas Práticas e Dicas Avançadas da JRT Technology Solutions

Ao longo de mais de uma década implementando soluções Oracle para clientes de todos os portes, a equipe da JRT consolidou um conjunto de boas prá

Quer aprender na prática com especialistas?

A JRT Technology Solutions oferece treinamentos e implementação de Oracle SQL para equipes corporativas.



Falar no WhatsApp

Thiago Paes Rodrigues

Com mais de 22 anos de experiência em Tecnologia da Informação, este profissional construiu uma trajetória sólida como empresário, atuando de forma estratégica na implementação de soluções tecnológicas que otimizam processos e impulsionam resultados em diferentes setores.