Bem-vindo a esta lição sobre subprogramas armazenados em Oracle PL/SQL, com um foco especial em funções. Você está prestes a embarcar em uma jornada que o ajudará a entender esses poderosos objetos de banco de dados e como eles podem facilitar sua vida como desenvolvedor ou administrador de banco de dados.
Entendendo Subprogramas Armazenados
Antes de mergulharmos nas funções, vamos entender o que são subprogramas armazenados. Imagine seu banco de dados como uma grande cozinha. Nesta cozinha, você tem várias receitas (suas instruções SQL) que usa para preparar dados. Agora, não seria conveniente se você pudesse pré-preparar algumas refeições e armazená-las no freezer, prontas para serem aquecidas e servidas quando necessário? É exatamente isso que os subprogramas armazenados fazem para o seu banco de dados.
Subprogramas armazenados são blocos PL/SQL nomeados que são armazenados no banco de dados. Eles podem ser chamados sempre que necessário, economizando seu tempo e esforço. Existem dois tipos principais de subprogramas armazenados: procedimentos e funções. Nesta lição, vamos nos concentrar nas funções, mas vamos falar brevemente sobre procedimentos para entender a diferença.
Procedimentos Armazenados vs. Funções Armazenadas
Pense nos procedimentos como eletrodomésticos que realizam tarefas. Um liquidificador, por exemplo, pega ingredientes e os processa, mas não necessariamente retorna algo. Da mesma forma, um procedimento pode receber parâmetros, realizar ações e opcionalmente retornar valores, mas retornar valores não é seu propósito principal.
As funções, por outro lado, são como ferramentas de medição na sua cozinha. Uma balança de cozinha, por exemplo, recebe uma entrada (o item que você coloca nela) e sempre retorna um valor (o peso). Da mesma forma, uma função em PL/SQL sempre retorna um valor, tornando-a ideal para cálculos ou recuperação de dados específicos.
Explorando Funções Armazenadas
Definição e Características
Uma função armazenada em Oracle PL/SQL é um bloco de código nomeado que é armazenado no banco de dados. Ela pode receber parâmetros, realizar operações e deve retornar um valor. Você pode pensar em uma função como uma receita reutilizável que sempre produz uma saída específica.
Criando uma Função Armazenada
Criar uma função é como adicionar uma nova ferramenta à sua caixa de ferramentas de banco de dados. Aqui está uma estrutura básica de como você pode criar uma função:
1 2 3 4 5 6 7 8 |
CREATE OR REPLACE FUNCTION nome_da_funcao (parametro1 tipo_de_dado, parametro2 tipo_de_dado) RETURN tipo_de_dado_retorno IS -- Seção de declaração BEGIN -- Seção executável RETURN valor; END nome_da_funcao; |
Invocando uma Função Armazenada
Depois de criar uma função, você pode chamá-la sempre que precisar. É como tirar aquela refeição pré-preparada do seu freezer e aquecê-la. Você pode usar uma função em instruções SQL ou dentro de outros blocos PL/SQL.
Removendo uma Função Armazenada
Às vezes, você pode precisar remover uma função que não usa mais. Isso é como limpar sua caixa de ferramentas para abrir espaço para novas ferramentas. Você pode remover uma função usando a instrução DROP FUNCTION:
1 |
DROP FUNCTION nome_da_funcao; |
Breve Visão Geral de Conceitos Avançados de Funções
À medida que você fica mais confortável com funções básicas, descobrirá que elas podem se tornar ainda mais poderosas quando colocadas em pacotes. Pacotes são como kits de ferramentas especializados que agrupam funções e procedimentos relacionados. Quando as funções fazem parte de um pacote, elas podem ganhar recursos e capacidades adicionais.
Conclusão
Nesta lição, você aprendeu sobre funções armazenadas em Oracle PL/SQL. Agora você entende que funções são blocos PL/SQL nomeados armazenados no banco de dados, muito parecidos com refeições pré-preparadas no seu freezer. Elas recebem parâmetros, realizam operações e sempre retornam um valor, tornando-as ideais para tarefas de cálculo e recuperação de dados.
Você viu como as funções diferem dos procedimentos, aprendeu o básico sobre criar, usar e remover funções, e teve um vislumbre de como as funções podem ser estendidas quando colocadas em pacotes.
À medida que você continua sua jornada em PL/SQL, lembre-se de que as funções são ferramentas poderosas que podem ajudá-lo a escrever código mais eficiente e fácil de manter. Pratique a criação e o uso de funções, e logo elas se tornarão uma parte indispensável do seu kit de desenvolvimento de banco de dados.
Funções Oracle PL/SQL: O Kit de Ferramentas Personalizado do Seu Banco de Dados
As Funções Oracle PL/SQL representam um componente fundamental na arquitetura de programação de bancos de dados Oracle. Estas estruturas modulares encapsulam lógica de negócios complexa, promovem a reutilização de código e melhoram significativamente a performance e a manutenibilidade de sistemas de banco de dados empresariais.
Aspectos Técnicos Principais:
- Encapsulamento de Lógica: As funções PL/SQL permitem o isolamento de operações específicas, facilitando a manutenção e depuração do código.
- Tipos de Retorno: Suportam uma ampla gama de tipos de dados Oracle, incluindo tipos escalares, compostos e objetos definidos pelo usuário.
- Parâmetros: Implementam passagem de parâmetros flexível, incluindo modos IN, OUT e IN OUT, permitindo interações complexas com o ambiente de chamada.
- Escopo e Visibilidade: Oferecem controle granular sobre o escopo de variáveis e sub-rotinas, melhorando a segurança e reduzindo conflitos de namespace.
- Otimização de Consultas: Quando utilizadas em consultas SQL, as funções PL/SQL podem ser integradas ao plano de execução do otimizador Oracle para melhorar o desempenho.
- Tratamento de Exceções: Incorporam mecanismos robustos de tratamento de erros, essenciais para a criação de aplicações de banco de dados resilientes.
- Integração com SQL: Podem ser invocadas diretamente em instruções SQL, expandindo as capacidades da linguagem de consulta estruturada.
Aqui está a estrutura básica que você usará:
1 2 3 4 5 6 7 8 9 10 |
CREATE [OR REPLACE] FUNCTION nome_da_funcao (parametro1 tipo_de_dado, parametro2 tipo_de_dado) RETURN tipo_de_dado_retorno IS -- Declarações de variáveis BEGIN -- As instruções da sua função vão aqui RETURN resultado; EXCEPTION -- Tratamento de exceções END; |
Parâmetros e Valores de Retorno em Funções PL/SQL
As funções PL/SQL são estruturas de código modulares projetadas para realizar operações específicas e retornar um resultado. Elas possuem dois componentes essenciais: parâmetros de entrada e valor de retorno.
- Parâmetros de Entrada:
- São variáveis declaradas na assinatura da função.
- Permitem que dados sejam passados para a função durante a chamada.
- Podem ser de diferentes tipos de dados (NUMBER, VARCHAR2, DATE, etc.).
- Suportam modos IN (padrão), OUT e IN OUT.
- Exemplo:
(p_employee_id IN NUMBER, p_salary IN OUT NUMBER)
- Valor de Retorno:
- É obrigatório em funções PL/SQL.
- Especificado pela cláusula RETURN na declaração da função.
- Pode ser de qualquer tipo de dado válido no Oracle, exceto LONG e LONG RAW.
- Retornado através da instrução RETURN no corpo da função.
- Exemplo:
RETURN NUMBER
Estrutura básica de uma função PL/SQL:
Exemplo: O Combinador de Nomes
Vamos criar uma função que combina um nome e um sobrenome em um nome completo. Vamos chamá-la de criar_nome_completo
:
1 2 3 4 5 6 7 8 9 10 11 12 |
CREATE OR REPLACE FUNCTION criar_nome_completo (p_nome IN VARCHAR2, p_sobrenome IN VARCHAR2) RETURN VARCHAR2 IS v_nome_completo VARCHAR2(100); BEGIN v_nome_completo := p_nome || ' ' || p_sobrenome; RETURN v_nome_completo; EXCEPTION WHEN OTHERS THEN RETURN 'Erro: Não foi possível criar o nome completo'; END; |
Esta função recebe o nome e sobrenome de uma pessoa e retorna seu nome completo. É como ensinar nosso banco de dados a se dirigir corretamente às pessoas. Vamos testá-lo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
-- Primeiro, certifique-se de que a função está criada executando o código de criação -- Agora, vamos executar a função DECLARE v_resultado VARCHAR2(100); BEGIN -- Chamando a função com dois nomes de exemplo v_resultado := criar_nome_completo('Maria', 'Silva'); -- Exibindo o resultado DBMS_OUTPUT.PUT_LINE('Nome completo: ' || v_resultado); -- Vamos testar com outros nomes v_resultado := criar_nome_completo('João', 'Santos'); DBMS_OUTPUT.PUT_LINE('Outro nome completo: ' || v_resultado); -- Testando com valores nulos (para verificar o tratamento de exceções) v_resultado := criar_nome_completo(NULL, NULL); DBMS_OUTPUT.PUT_LINE('Teste com NULL: ' || v_resultado); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Ocorreu um erro: ' || SQLERRM); END; / |
Observações:
- Este código testa a função com diferentes entradas, incluindo nomes normais e valores nulos.
- O tratamento de exceções na função retornará uma mensagem de erro se algo der errado, mas no exemplo dado, provavelmente não veremos essa mensagem, pois os casos de teste são simples.
- Se você quiser testar o tratamento de exceções mais diretamente, poderia modificar a função para lançar uma exceção em certas condições e então testar essas condições.
Exemplo: O Detetive de Números Primos
Agora, vamos criar uma função que determina se um número é primo. Vamos chamá-la de e_primo
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
CREATE OR REPLACE FUNCTION e_primo (p_num IN NUMBER) RETURN BOOLEAN IS v_i NUMBER; BEGIN IF p_num <= 1 THEN RETURN FALSE; END IF; FOR v_i IN 2..SQRT(p_num) LOOP IF MOD(p_num, v_i) = 0 THEN RETURN FALSE; END IF; END LOOP; RETURN TRUE; EXCEPTION WHEN OTHERS THEN RETURN FALSE; END; |
Esta função é como um detetive que rapidamente nos diz se um número é primo (verdadeiro) ou não (falso).
Quando Algo Dá Errado: Tratamento de Exceções
Às vezes, as coisas não saem como planejado. É por isso que adicionamos tratamento de exceções às nossas funções. É como adicionar uma rede de segurança para pegar quaisquer erros:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
CREATE OR REPLACE FUNCTION divisao_segura (p_a IN NUMBER, p_b IN NUMBER) RETURN NUMBER IS v_resultado NUMBER; BEGIN v_resultado := p_a / p_b; RETURN v_resultado; EXCEPTION WHEN ZERO_DIVIDE THEN RETURN NULL; WHEN OTHERS THEN RETURN NULL; END; |
Esta função verifica se estamos tentando dividir por zero (o que é um erro em matemática) e retorna NULL se cometemos esse erro ou se ocorrer qualquer outro erro.
Utilização de Funções Oracle PL/SQL
Colocando Seus Assistentes para Trabalhar
Agora que criamos nossos assistentes de banco de dados úteis, é hora de colocá-los para trabalhar. Você pode usar funções em várias partes de seus blocos PL/SQL e instruções SQL, assim como usaria diferentes ferramentas para diferentes trabalhos em casa.
Exemplo: Criando Nomes Completos
Vamos usar nossa função criar_nome_completo
em um bloco PL/SQL:
1 2 3 4 5 6 |
DECLARE v_nome_completo VARCHAR2(100); BEGIN v_nome_completo := criar_nome_completo('João', 'Silva'); DBMS_OUTPUT.PUT_LINE('Nome completo: ' || v_nome_completo); END; |
Isso exibirá: “Nome completo: João Silva”
Exemplo: Encontrando Números Primos
Podemos usar nossa função e_primo
para verificar se um número é primo:
1 2 3 4 5 6 7 8 9 |
DECLARE v_numero NUMBER := 17; BEGIN IF e_primo(v_numero) THEN DBMS_OUTPUT.PUT_LINE(v_numero || ' é primo'); ELSE DBMS_OUTPUT.PUT_LINE(v_numero || ' não é primo'); END IF; END; |
Este bloco produzirá: “17 é primo”
Funções em Instruções SELECT: Adicionando Informações Extras
As funções podem adicionar informações extras aos resultados de sua consulta. Por exemplo:
1 2 3 4 5 |
SELECT id_funcionario, criar_nome_completo(nome, sobrenome) AS nome_completo FROM funcionarios; |
Esta consulta busca IDs de funcionários e usa nossa função para criar nomes completos.
Funções em Declarações Condicionais: Tomada de Decisão Inteligente
Você pode usar funções para criar condições inteligentes em seus blocos PL/SQL:
1 2 3 4 5 6 7 8 9 |
DECLARE v_numero NUMBER := 23; BEGIN IF e_primo(v_numero) THEN DBMS_OUTPUT.PUT_LINE(v_numero || ' é um número primo'); ELSE DBMS_OUTPUT.PUT_LINE(v_numero || ' não é um número primo'); END IF; END; |
Este bloco usa nossa função e_primo
para determinar se 23 é primo.
Conclusão
Parabéns. Você acabou de aprender como criar e usar suas próprias funções Oracle PL/SQL. Esses assistentes personalizados podem tornar seu trabalho com banco de dados mais fácil, rápido e organizado. Lembre-se, as funções são como seu kit de ferramentas pessoal de banco de dados – quanto mais você praticar usá-las, mais habilidoso se tornará na resolução de desafios de banco de dados de forma eficiente.
Utilizando Funções em Instruções SQL e Escrevendo Funções Complexas
Introdução
Nesta aula, você aprenderá a invocar funções em instruções SQL e a escrever funções mais complexas. Essas habilidades são fundamentais para aproveitar todo o potencial das funções PL/SQL em suas operações de banco de dados.
Invocando Funções em Instruções SQL
As funções retornam um único valor e podem ser muito úteis em uma instrução SELECT. Em particular, elas podem ajudar você a evitar instruções SQL complexas repetidas dentro de uma instrução SELECT.
Exemplo Básico
Primeiro, vamos criar a função get_full_name
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
CREATE OR REPLACE FUNCTION get_full_name(p_employee_id IN hr.employees.employee_id%TYPE) RETURN VARCHAR2 IS v_full_name VARCHAR2(200); BEGIN SELECT first_name || ' ' || last_name INTO v_full_name FROM hr.employees WHERE employee_id = p_employee_id; RETURN v_full_name; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 'Funcionário não encontrado'; WHEN OTHERS THEN RETURN 'Erro ao obter o nome completo'; END get_full_name; |
Invocação da Função em uma Instrução SQL
Agora, podemos usar a função get_full_name
em uma instrução SELECT
:
1 2 3 |
SELECT employee_id, get_full_name(employee_id) AS full_name FROM hr.employees WHERE department_id = 10; |
Explicação do Código
- Função
get_full_name
:- Esta função recebe um
employee_id
como parâmetro e retorna o nome completo do empregado concatenandofirst_name
elast_name
. - Usa uma consulta
SELECT
para buscar os dados a partir da tabelaemployees
. - Inclui tratamento básico de exceções para lidar com casos em que o funcionário não é encontrado ou se ocorrem outros erros.
- Esta função recebe um
- Instrução
SELECT
:- A consulta seleciona
employee_id
e usa a funçãoget_full_name
para adicionar uma colunafull_name
que contém o nome completo dos empregados do departamento comdepartment_id = 10
. - Isso demonstra como uma função PL/SQL pode simplificar a complexidade de uma instrução SQL, encapsulando a lógica de concatenação de nomes dentro de uma função reutilizável.
- A consulta seleciona
Essa abordagem de usar funções em instruções SQL torna o código mais limpo e eficiente, evitando a repetição de lógica de manipulação de strings em várias partes do seu SQL.
Funções Embutidas
Na verdade, você já vem usando funções em instruções SQL, talvez sem perceber. Por exemplo, a função UPPER
:
1 |
SELECT UPPER('joão') FROM DUAL; |
Esta função Oracle retorna o valor em maiúsculas do parâmetro passado.
Requisitos para Funções em SQL
Para uma função definida pelo usuário ser chamada em uma expressão SQL:
- Deve ser uma função de LINHA, não uma função de GRUPO
- Os tipos de dados devem ser tipos de dados SQL (não tipos PL/SQL como Boolean, table ou record)
- A função não pode incluir instruções DML (INSERT, UPDATE, DELETE)
Escrevendo Funções Complexas
As funções podem se tornar bastante elaboradas e complexas. Vamos examinar um exemplo mais avançado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
CREATE OR REPLACE FUNCTION novo_id_empregado RETURN hr.employees.employee_id%TYPE AS v_novo_id hr.employees.employee_id%TYPE; BEGIN SELECT EMPLOYEES_SEQ.NEXTVAL INTO v_novo_id FROM dual; RETURN v_novo_id; EXCEPTION WHEN OTHERS THEN DECLARE v_sqlerrm VARCHAR2(250) := SUBSTR(SQLERRM, 1, 250); BEGIN RAISE_APPLICATION_ERROR(-20003, 'Erro ao gerar employee_id: ' || v_sqlerrm); END; END novo_id_empregado; |
Executando a Função:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DECLARE v_novo_id hr.employees.employee_id%TYPE; BEGIN -- Chama a função para obter um novo ID de empregado v_novo_id := novo_id_empregado; -- Exibe o novo ID gerado DBMS_OUTPUT.PUT_LINE('Novo ID de Empregado: ' || v_novo_id); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Erro ao chamar a função: ' || SQLERRM); END; / |
Análise do Exemplo:
- Tipo de Retorno Baseado em Coluna: A função retorna um valor do tipo
employee_id
da tabelaEMPLOYEES
no schema HR. - Uso de Sequência: Utiliza
EMPLOYEES_SEQ
para gerar um novo valor de ID único. É importante garantir que essa sequência existe no schema HR para que o exemplo funcione corretamente. - Tratamento de Exceções Robusto: Captura e relata erros usando
RAISE_APPLICATION_ERROR
, tornando o manejo de erros mais claro e útil para o usuário.
Função Complexa: get_employee_details
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
CREATE OR REPLACE FUNCTION get_employee_details(p_employee_id IN hr.employees.employee_id%TYPE) RETURN VARCHAR2 IS v_employee_details VARCHAR2(400); BEGIN SELECT e.first_name || ' ' || e.last_name || ' - ' || j.job_title || ', ' || d.department_name INTO v_employee_details FROM hr.employees e JOIN hr.jobs j ON e.job_id = j.job_id JOIN hr.departments d ON e.department_id = d.department_id WHERE e.employee_id = p_employee_id; RETURN v_employee_details; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 'Detalhes do funcionário não encontrados'; WHEN OTHERS THEN RETURN 'Erro ao obter detalhes do funcionário'; END get_employee_details; |
Explicação do Código
- Objetivo da Função: Retornar uma string que concatena o nome completo do empregado, seu título de emprego e o nome do departamento.
- Consulta SQL:
- Estamos utilizando
JOIN
para unir as tabelasEMPLOYEES
,JOBS
, eDEPARTMENTS
, utilizandojob_id
edepartment_id
como chaves estrangeiras. - A função forma uma string que inclui o
first_name
,last_name
,job_title
, edepartment_name
.
- Estamos utilizando
- Tratamento de Exceções:
NO_DATA_FOUND
: Retorna uma mensagem amigável se oemployee_id
não corresponder a nenhum registro.OTHERS
: Captura outros erros potenciais e retorna uma mensagem genérica de erro.
Uso da Função em uma Instrução SQL
Agora, podemos chamar a função get_employee_details
em uma instrução SELECT
para obter informações formatadas sobre os funcionários:
1 2 3 |
SELECT employee_id, get_employee_details(employee_id) AS employee_details FROM hr.employees WHERE department_id = 10; |
Explicação da Instrução SQL
- Consulta: Seleciona
employee_id
e chamaget_employee_details
para gerar uma descrição formatada de cada empregado no departamento comdepartment_id = 10
.
Essa função complexa demonstra como podemos integrar diferentes informações de várias tabelas em uma string formatada, tornando as consultas mais legíveis e simplificando a lógica do aplicativo ao mover a complexidade para a função PL/SQL.
Pontos-chave sobre Funções Complexas
- Tipos de Retorno Dinâmicos: Usar
%TYPE
permite que a função se adapte automaticamente a mudanças no esquema do banco de dados. - Uso de Sequências: Sequências são uma maneira eficiente de gerar valores únicos para IDs.
- Tratamento de Exceções Robusto: A função captura qualquer erro e o relata de forma mais clara e útil.
- Reutilização de Código: Funções como esta podem ser usadas em várias partes do seu código, promovendo consistência e facilitando a manutenção.
Conclusão
Funções em PL/SQL são ferramentas poderosas que podem simplificar suas consultas SQL e encapsular lógica complexa. Ao usar funções em instruções SQL, você pode tornar seu código mais legível e manutenível. Já ao escrever funções complexas, você pode criar componentes reutilizáveis que melhoram a eficiência e a robustez de suas aplicações de banco de dados.
Lembre-se sempre de considerar o desempenho ao usar funções em SQL, especialmente em conjuntos de dados grandes. Em alguns casos, pode ser mais eficiente incorporar a lógica diretamente na consulta SQL, enquanto em outros, uma função bem projetada pode melhorar significativamente a clareza e a manutenibilidade do seu código.