Definição de Tabelas e Registros de PL/SQL

Oracle PL/SQL

Com este artigo pretendo demonstrar a utilização de uma forma semelhante a array (vetor) em PL/SQL do Oracle. Esta funcionalidade é baseada nos recursos TABLE e RECORD (para criação de arrays mais complexos – com mais de uma variável) de PL/SQL, discutidos no decorrer deste artigo. As variáveis de tabelas também são conhecidas como arrays, index-by tables ou PL/SQL tables.

Tabelas

Tabelas PL/SQL são modeladas de forma semelhante às tabelas do banco, no entanto não é possível utilizar instruções SQL para manipulação de dados destas tabelas. As tabelas PL/SQL poderão possuir uma chave primária, que irá dar acesso às linhas. A chave e as linhas podem ser consideradas como o índice e os elementos de um vetor unidimensional, ou seja, assim como um vetor, uma tabela PL/SQL é um conjunto ordenado de elementos do mesmo tipo. Cada elemento possui um número de índice diferente, o que determina a sua posição no conjunto ordenado. Uma tabela PL/SQL difere de vetores tradicionais em dois pontos principais:

• Ao contrário dos vetores, que possuem limites inferior e superior bem definidos, as tabelas PL/SQL não possuem limites, podendo crescer dinamicamente;
• Os índices não precisam, necessariamente, ser sucessivos, podendo assumir qualquer valor para ordenação (por exemplo, código do cliente).

A cláusula INDEX BY especifica um tipo BINARY_INTEGER, que possui uma abrangência de valores entre -2147483647 a 2147483647, caso o tipo de dado seja um registro, cada um dos campos deste registro deve ser de um tipo escalar, como CHAR, NUMBER ou DATE.

Para especificar o tipo de dado do elemento, pode ser utilizado um tipo escalar fixo ou uma herança, como uso de %Type e %RowType.

A cláusula NOT NULL pode ser utilizada para garantir que não serão armazenados dados nulos nas linhas da tabela.

Vale observar que até o momento que um elemento receba um valor, ele não existe. Caso um elemento inexistente seja referenciado, uma exceção NO_DATA_FOUND será disparada.

Para referenciar elementos de uma tabela de PL/SQL utiliza-se a seguinte sintaxe:
Nome_da_tabela (índice)

Ou

-- Para tabelas de registros
Nome_da_tabela (índice).Nome_do_campo

Declaração:
TYPE nome_do_tipo_tabela
IS TABLE OF tipo_de_dado [NOT NULL]
INDEX BY BINARY_INTEGER

Listagem 1. Exemplo de PL utilizando tabela de PL/SQL

Declare

TYPE Tp_Tab_Cli
IS TABLE OF Cliente%ROWTYPE
INDEX BY BINARY_INTEGER;
Cli_tab1 Tp_Tab_Cli;
Cli_tab2 Tp_Tab_Cli;
C Binary_Integer;
Total Binary_Integer;
Maior Fornecedor.Nr_Fornecedor%Type;
Begin
C := 1;
-- Popula a tabela PL/SQL Cli_Tab1
For RecCli in (Select * from Cliente) Loop
Cli_Tab1(C).Nm_Cliente := RecCli.Nm_Cliente;
Cli_Tab1(C).Sg_Estado := RecCli.Sg_Estado;
Cli_Tab1(C).Ds_Endereco := RecCli.Ds_Endereco;
Cli_Tab1(C).Cd_Municipio := RecCli.Cd_Municipio;
Cli_Tab1(C).Nr_Fone := RecCli.Nr_Fone;
C := C + 1;
End Loop ;
Total := C – 1;
-- Copia o conteúdo de uma tabela PL/SQL para outra
Cli_tab2 := Cli_tab1;
For C IN 1..Total Loop
If Cli_tab1(C).sg_estado = ‘SC'Then
Select Max(Nr_Fornecedor) + 1
Into Maior
From Fornecedor;
--
Insert into Fornecedor
(Nr_Fornecedor, Nm_Fornecedor, Sg_Estado,
Ds_Endereco, Cd_Municipio, Nr_Fone)
Values
(Maior, Cli_Tab1(C).Nm_Cliente,
Cli_Tab1(C).Sg_Estado, Cli_Tab1(C).Ds_Endereco,
Cli_Tab1(C).Cd_Municipio, Cli_Tab1(C).Nr_Fone);
End If;
End Loop ;
Commit;
End;


As tabelas de PL/SQL possuem um conjunto de atributos que tornam mais fácil seu uso e manutenção. Os atributos de uma tabela são utilizados colocando-se o seu nome precedido de ponto logo após o nome da tabela PL/SQL:
Nome_da_tabela.Nome_do_atributo

Os atributos disponíveis para as tabelas PL/SQL são:

• EXISTS: Retorna TRUE caso o elemento do índice especificado de uma tabela PL/SQL exista (Nome_da_tabela.EXISTS(índice)). Este atributo é, muitas vezes, utilizado para evitar exceções do tipo NO_DATA_FOUND;
• COUNT: Retorna o número de elementos que uma tabela PL/SQL contém;
• FIRST: Retorna o valor do índice do primeiro elemento da tabela PL/SQL. Caso a tabela esteja vazia irá retornar NULL (i BINARY_INTEGER := Tb_Cliente.FIRST);
• LAST: Retorna o valor do índice do último elemento da tabela PL/SQL. Caso a tabela esteja vazia irá retornar NULL;
• PRIOR: Retorna o número de índice que precede o índice especificado, caso o índice especificado seja o primeiro, irá retornar NULL;
• NEXT: Retorna o número de índice que sucede o índice especificado, caso o índice especificado seja o último, irá retornar NULL (i := Tb_Cliente.NEXT(i));
• DELETE: Este atributo pode ser utilizado de três formas:
• DELETE: Apaga toda a tabela PL/SQL especificada;
• DELETE (n): Apaga o elemento n da tabela PL/SQL especificada;
• DELETE (m, n): Apaga os elementos cujo índice vai de m até n na tabela PL/SQL especificada.

Uma opção para o uso de tabelas PL/SQL é a simulação de operações sobre tabelas de base, para isso inicialmente pode-se carregar dados da tabela de base para a tabela PL/SQL.

Registros

Um registro pode ser declarado através do uso do atributo %ROWTYPE, onde o registro será criado com base no registro de uma tabela ou no registro resultante de uma seleção de um cursor. No entanto, registros podem ser declarados com quaisquer definições de campos e, para isso, a declaração de tipo RECORD é utilizada. Para referenciar um campo individual de um registro, utiliza-se o nome do registro seguido de um ponto e o nome do campo ao qual se deseja referenciar:
Nome_do_registro.Nome_do_campo

Declaração:
TYPE nome_do_tipo IS RECORD
(nome_do_campo tipo_do_campo [NOT NULL]
[[:= / DEFAULT] expressão], ...)

Listagem 2. Exemplo de PL utilizando registro

Declare
Type Tp_Rec_Hora Is RECORD (
Hora Smallint := 0,
Minuto Smallint := 0,
Segundo Smallint := 0);
Type Local_Hora Is RECORD (
Dia Date,
Horario Tp_Rec_Hora, -- Registro Aninhado
Local Varchar2(20));
Type Tp_Tab_Hora
IS TABLE OF Tp_Rec_Hora
INDEX BY BINARY_INTEGER;
Declare
Type Tp_Rec_Endereco Is RECORD (
Ds_Endereco Varchar2(50),
Cd_Municipio Number(5),
Sg_Estado Char(2),
Nr_Cep Varchar2(8));
Endereco Tp_Rec_Endereco;

Nome Cliente.Nm_Cliente%Type;
Begin
Select Nm_Cliente
Into Nome
From Cliente
Where Cd_Cliente = 1;
--
Select Ds_Endereco, Cd_Municipio,
Sg_Estado, Nr_Cep
Into Endereco
From Cliente
Where Cd_Cliente = 1;
Dbms_Output.Put_Line(Nome||' residente a ‘||Endereco.ds_endereco||
‘ – ‘||Endereco.Sg_Estado);
End;

Conclusões

O uso de vetores em PL/SQL possibilitam, em alguns casos, ganho de performance para a execução do procedimento, além de possibilitar simulações baseadas em variações dos dados existentes nas tabelas de banco de dados, sem a necessidade de gerar novos objetos de banco de dados.