Nesse artigo vamos aprender sobre os Registros Baseados em Tabelas e Cursores em Oracle PL/SQL. Prepare-se para um artigo detalhado, repleto de exemplos práticos e insights valiosos! Para executar os exemplos desse artigo, certifique-se de criar os objetos conforme mostrado nesse artigo aqui.
Introdução aos Registros
Antes de entrarmos nos tipos específicos, vamos entender o que são registros em PL/SQL. Um registro é uma estrutura de dados que se assemelha a uma linha de uma tabela de banco de dados. Cada item de dado é armazenado em um campo com seu próprio nome e tipo de dado.
Imagine que você está organizando informações sobre empresas. Cada empresa tem um nome, endereço e número de funcionários. Um registro contendo um campo para cada um desses itens permite que você trate uma empresa como uma unidade lógica, facilitando a organização e representação das informações da empresa.
A estrutura de um registro é semelhante a esta:
1 2 3 4 5 |
TYPE empresa_record IS RECORD ( nome VARCHAR2(100), endereco VARCHAR2(200), num_funcionarios NUMBER ); |
Esta estrutura nos permite tratar os dados da empresa como uma única unidade lógica, tornando mais fácil organizar e representar as informações.
Registros Baseados em Tabelas
O atributo %ROWTYPE nos permite criar registros baseados em tabelas e cursores. É semelhante ao atributo %TYPE usado para definir variáveis escalares, mas em vez de definir um único campo, ele define uma estrutura completa baseada em uma linha da tabela ou no resultado de um cursor.
Exemplo Detalhado de Registro Baseado em Tabela
Vamos ver um exemplo prático e detalhado:
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 27 28 29 30 31 32 |
DECLARE curso_rec curso%ROWTYPE; BEGIN -- Recupera dados de um curso específico SELECT * INTO curso_rec FROM curso WHERE numero_curso = 25; -- Exibe informações do curso DBMS_OUTPUT.PUT_LINE('Detalhes do Curso:'); DBMS_OUTPUT.PUT_LINE('------------------'); DBMS_OUTPUT.PUT_LINE('Número do Curso: ' || curso_rec.numero_curso); DBMS_OUTPUT.PUT_LINE('Descrição do Curso: ' || curso_rec.descricao); DBMS_OUTPUT.PUT_LINE('Pré-requisito: ' || NVL(TO_CHAR(curso_rec.pre_requisito), 'Nenhum')); DBMS_OUTPUT.PUT_LINE('Créditos: ' || curso_rec.creditos); DBMS_OUTPUT.PUT_LINE('Departamento: ' || curso_rec.departamento); DBMS_OUTPUT.PUT_LINE('Custo: $' || TO_CHAR(curso_rec.custo, '999,999.00')); -- Demonstração de manipulação de dados no registro IF curso_rec.pre_requisito IS NULL THEN DBMS_OUTPUT.PUT_LINE('Este curso não tem pré-requisitos.'); ELSE DBMS_OUTPUT.PUT_LINE('Este curso tem pré-requisito: Curso ' || curso_rec.pre_requisito); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Erro: Curso não encontrado.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Erro inesperado: ' || SQLERRM); END; |
Neste exemplo:
curso_rec
tem a mesma estrutura que uma linha na tabela CURSO.- Usamos SELECT * para preencher todos os campos do registro de uma só vez.
- Demonstramos como acessar campos individuais usando a notação de ponto.
- Incluímos tratamento de exceções para melhor robustez.
Atenção! Limitações dos Registros
É crucial entender que um registro não tem um valor próprio. Isso significa que você não pode realizar certas operações diretamente nos registros. Por exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
DECLARE curso_rec1 curso%ROWTYPE; curso_rec2 curso%ROWTYPE; BEGIN -- As seguintes operações causariam erros de sintaxe: -- IF curso_rec1 IS NULL THEN -- DBMS_OUTPUT.PUT_LINE('Registro nulo'); -- END IF; -- IF curso_rec1 = curso_rec2 THEN -- DBMS_OUTPUT.PUT_LINE('Registros iguais'); -- END IF; -- Em vez disso, você deve comparar campos individuais: IF curso_rec1.numero_curso = curso_rec2.numero_curso AND curso_rec1.descricao = curso_rec2.descricao THEN DBMS_OUTPUT.PUT_LINE('Cursos têm o mesmo número e descrição'); END IF; END; |
Registros Baseados em Tabelas com Cursores Explícitos
Agora, vamos explorar como usar registros baseados em tabelas com cursores explícitos. Isso nos permite processar múltiplas linhas de uma tabela de forma eficiente.
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 27 |
DECLARE CURSOR c_curso RETURN curso%ROWTYPE IS SELECT * FROM curso WHERE rownum <= 5; -- limita o conjunto de resultados a 5 linhas curso_rec curso%ROWTYPE; BEGIN OPEN c_curso; DBMS_OUTPUT.PUT_LINE('Top 5 Cursos:'); DBMS_OUTPUT.PUT_LINE('-------------'); LOOP FETCH c_curso INTO curso_rec; EXIT WHEN c_curso%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Curso: ' || curso_rec.numero_curso); DBMS_OUTPUT.PUT_LINE('Descrição: ' || curso_rec.descricao); DBMS_OUTPUT.PUT_LINE('Pré-requisito: ' || NVL(TO_CHAR(curso_rec.pre_requisito), 'Nenhum')); DBMS_OUTPUT.PUT_LINE('Créditos: ' || curso_rec.creditos); DBMS_OUTPUT.PUT_LINE('-----------------------------'); END LOOP; CLOSE c_curso; -- Demonstração de uso do %ROWCOUNT DBMS_OUTPUT.PUT_LINE('Total de cursos processados: ' || c_curso%ROWCOUNT); END; |
Neste script:
- O cursor explícito
c_curso
é declarado para retornar um registro baseado na tabela curso. - Usamos um loop para processar cada linha retornada pelo cursor.
- Demonstramos o uso do atributo %ROWCOUNT para contar as linhas processadas.
Registros Baseados em Cursores
Um registro baseado em cursor tem a mesma estrutura que o cursor explícito no qual é baseado. Isso nos permite trabalhar com um subconjunto específico de colunas ou com resultados de consultas complexas.
Exemplo Detalhado de Registro Baseado em Cursor
Vamos ver um exemplo prático e detalhado:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
DECLARE CURSOR c_tipo_nota IS SELECT codigo_tipo_nota, descricao, peso FROM tipo_nota WHERE ativo = 'S' ORDER BY peso DESC; tipo_nota_rec c_tipo_nota%ROWTYPE; v_total_peso NUMBER := 0; BEGIN OPEN c_tipo_nota; DBMS_OUTPUT.PUT_LINE('Tipos de Nota Ativos:'); DBMS_OUTPUT.PUT_LINE('---------------------'); LOOP FETCH c_tipo_nota INTO tipo_nota_rec; EXIT WHEN c_tipo_nota%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Código: ' || tipo_nota_rec.codigo_tipo_nota); DBMS_OUTPUT.PUT_LINE('Descrição: ' || tipo_nota_rec.descricao); DBMS_OUTPUT.PUT_LINE('Peso: ' || tipo_nota_rec.peso); DBMS_OUTPUT.PUT_LINE('-----------------------------'); v_total_peso := v_total_peso + tipo_nota_rec.peso; END LOOP; -- Demonstração de uso dos atributos do cursor IF c_tipo_nota%FOUND THEN DBMS_OUTPUT.PUT_LINE('Total de tipos de nota: ' || c_tipo_nota%ROWCOUNT); DBMS_OUTPUT.PUT_LINE('Soma total dos pesos: ' || v_total_peso); ELSE DBMS_OUTPUT.PUT_LINE('Nenhum tipo de nota ativo encontrado.'); END IF; CLOSE c_tipo_nota; EXCEPTION WHEN OTHERS THEN IF c_tipo_nota%ISOPEN THEN CLOSE c_tipo_nota; END IF; RAISE; END; |
Neste exemplo:
- O cursor
c_tipo_nota
seleciona um subconjunto de colunas e aplica filtros e ordenação. - Demonstramos o uso de um registro baseado em cursor para processar os resultados.
- Incluímos cálculos adicionais (soma dos pesos) para mostrar como manipular os dados.
- Utilizamos os atributos %FOUND e %ROWCOUNT do cursor para fornecer informações adicionais.
Importante! Ordem de Declaração
É crucial lembrar que a declaração de um registro baseado em cursor deve ser precedida pela declaração do cursor. Veja um exemplo de erro comum:
1 2 3 4 5 6 7 8 9 10 11 |
DECLARE -- Isso causará um erro: tipo_nota_rec c_tipo_nota%ROWTYPE; CURSOR c_tipo_nota IS SELECT codigo_tipo_nota, descricao FROM tipo_nota; BEGIN NULL; -- Código omitido END; |
Este exemplo produzirá um erro de compilação, pois o registro é declarado antes do cursor.
Dicas Avançadas e Melhores Práticas
- Use registros baseados em tabelas quando precisar trabalhar com todas ou a maioria das colunas de uma tabela.
- Opte por registros baseados em cursores quando precisar de um subconjunto específico de colunas ou estiver trabalhando com junções complexas.
- Ao trabalhar com grandes conjuntos de dados, considere o uso de BULK COLLECT com registros para melhorar o desempenho:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DECLARE TYPE curso_tab_type IS TABLE OF curso%ROWTYPE; curso_tab curso_tab_type; BEGIN SELECT * BULK COLLECT INTO curso_tab FROM curso WHERE rownum <= 100; FOR i IN 1..curso_tab.COUNT LOOP DBMS_OUTPUT.PUT_LINE('Curso: ' || curso_tab(i).numero_curso); END LOOP; END; |
Utilize FOR loops com cursores para simplificar o código e evitar erros comuns de gerenciamento de cursores:
1 2 3 4 5 |
BEGIN FOR curso_rec IN (SELECT * FROM curso WHERE rownum <= 5) LOOP DBMS_OUTPUT.PUT_LINE('Curso: ' || curso_rec.numero_curso); END LOOP; END; |
Ao trabalhar com registros em procedimentos ou funções, considere usar parâmetros do tipo IN OUT para modificar os dados do registro:
1 2 3 4 5 6 7 8 |
PROCEDURE atualizar_curso (p_curso IN OUT curso%ROWTYPE) IS BEGIN p_curso.creditos := p_curso.creditos + 1; -- Atualizar no banco de dados UPDATE curso SET creditos = p_curso.creditos WHERE numero_curso = p_curso.numero_curso; END; |
Conclusão
Ufa! Chegamos ao fim desta jornada detalhada pelos registros baseados em tabelas e cursores em PL/SQL. Vimos que esses registros são ferramentas poderosas que nos permitem trabalhar com dados de forma estruturada e eficiente.
Pontos-chave para lembrar:
- Registros baseados em tabelas usam %ROWTYPE de uma tabela e são ideais para trabalhar com estruturas de tabelas completas.
- Registros baseados em cursores usam %ROWTYPE de um cursor e são perfeitos para trabalhar com subconjuntos de dados ou consultas complexas.
- Registros não têm valor próprio, mas seus campos individuais têm. Isso afeta como podemos manipulá-los e compará-los.
- A ordem de declaração é crucial: sempre declare o cursor antes de declarar um registro baseado nele.
- Utilize as melhores práticas, como BULK COLLECT e FOR loops com cursores, para otimizar seu código PL/SQL.
Na próxima aula, exploraremos técnicas avançadas de manipulação de dados usando registros e cursores, incluindo o uso de coleções e tipos de objetos em PL/SQL.