Buscar

Utilizando Vetores e Matrizes com PLSQL - Tabelas aninhadas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 22 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 22 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 22 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

USING COLLECTIONS AND RECORDS (PARTE 02) – NESTED TABLES 
Do que se trata este artigo
	Trata-se de um artigo de como utilizar as coleções e as variáveis de registros do PL/SQL.
Em que situação o tema é útil:
	Esta é uma técnica de programação muito utilizada nos casos em que o desenvolvedor necessita armazenar uma coleção de dados, como uma tabela virtual, por exemplo. Alocando um espaço na memória do servidor e acessando-o posteriormente, podendo tornar o próprio desenvolvimento mais eficiente e mais eficaz. E tratando-se de um espaço na memória e não no disco (seja ele rígido ou sólido), dispensando a necessidade de gravação, leitura e limpeza do espaço no disco, onde é muito mais ineficiente (mesmo sendo um disco SSD) comparando com o desempenho que se obtém pela memória.	
	
No artigo anterior iniciamos uma sequência de três artigos, onde iremos abordar os tipos de coleções do PL/SQL, sendo eles: VARRAY – matrizes e vetores -, NESTED TABLES - Tabelas Aninhadas - e Arrays associativos. Nesta continuação da série de artigos, iremos abordar o conceito de NESTED TABLES - Tabelas Aninhadas - no PL/SQL.
Uma tabela aninhada nada mais é do que uma tabela incorporada dentro de outra, onde você pode inserir, atualizar e excluir elementos (e não registros, lembre-se sempre disto) de forma individual em uma tabela aninhada, isto é, as tabelas aninhadas são flexíveis, ao contrário de um VARRAY, onde os elementos ali contidos podem apenas serem modificados como um todo e devem conter um número fixo de elementos. Uma tabela aninhada não possuí um tamanho máximo de elementos e o desenvolvedor pode armazenar um número de elementos conforme a sua necessidade. (PRICE, 2009)
As linhas de uma tabela aninhada são armazenadas sem nenhuma ordem específica, mas quando você recupera as informações de uma tabela aninhada em uma variável PL/SQL, as linhas recebem subscritos consecutivos começando em 1, isto torna a tabela aninhada uma matriz a linhas individuais.
Inicialmente as tabelas aninhadas são densas, assim como as matrizes, que não é possível excluir elementos individuais de uma matriz, mas no caso das tabelas aninhadas é possível efetuar esta exclusão de elementos de forma individual, isto torna a tabela aninhada dinâmica, onde, claro que deixará lacunas nos seus índices, mas o Oracle entenderá que o próximo elemento da tabela estará no próximo índice preenchido com algum elemento, ou seja, mesmo estando nulo (null) o elemento o Oracle percorrerá o índice para próximo, e assim sucessivamente até encontrar o próximo índice preenchido por algum elemento que não esteja nulo (null). (MOORE, 2009)
Conforme a documentação do Oracle, existem algumas vantagens e desvantagens em se utilizar uma tabela aninhada, comparado com as VARRAYS e Matrizes Associativas, quando: 
· Uma tabela aninhada pode ser armazenada em uma coluna do banco de dados, ou seja, você pode usar a tabela aninhada para simplificar as operações em SQL que ingressar em uma tabela de coluna única com uma tabela maior.
· Os valores do índice não são consecutivos, ou possa possuir valores nulos dentro dos elementos dos índices;
· Não há um número definido de valores de índice, ou seja, não possuí limite de tamanho e não há a necessidade de fixar o tamanho da tabela;
· Deve-se excluir ou atualizar alguns elementos, mas não todos os elementos ao mesmo tempo, ao contrário do VARRAY;
· Poderia ser criado uma tabela de pesquisa separada, com várias entradas para cada linha da tabela principal, e acessá-lo através de consultas de associação;
· Os dados de uma tabela aninhada são armazenados em uma tabela de armazenamento, uma tabela gerada pelo próprio sistema do banco de dados. Quando é efetuado o acesso há uma tabela aninhada, o banco de dados junta a tabela aninhada com sua tabela de armazenamento, tornando-as próprias para as consultas e atualizações que afetam apenas alguns elementos da coleção; 
· A desvantagem de uma tabela aninhada é que, não se pode confiar na ordem dos índices de uma tabela aninhada, isto porque, pode-se excluir e incluir novos índices de forma dinâmica, e o banco de dados não preserva a ordem dos elementos. (MOORE, 2009)
Os comandos que podem ser utilizados para as NESTED TABLES são os mesmos que para os VARRAYS, veja abaixo a mesma tabela que foi mostrada no artigo anterior (apenas para recordar): (BURLESON, 2008)
	COUNT
	Retorna o número de elementos de um VARRAY.
	EXISTS
	Retorna um valor booleano verdadeiro se o elemento no índice especificado exista, caso contrário retornará falso.
	EXTEND
	Aumenta o tamanho da matriz por 1 ou pelo número especificado. Não pode ser usado em matrizes associativas.
	FIRST
	Navega para o primeiro elemento da matriz.
	LAST
	Navega para o último elemento da matriz.
	NEXT
	Navega para o próximo elemento da matriz.
	PRIOR
	Navega para o elemento anterior da matriz.
	TRIM
	Remove o último elemento da matriz. Não pode ser usado em matrizes associativas.
	DELETE
	Remove todos os elementos de uma matriz.
	LIMIT
	Mostra o limite de tamanho de uma matriz.
O comando abaixo, é criado uma NESTED TABLE do tipo VARCHAR2 com o nome de TABLE_NAMES, e acrescentado alguns nomes para efetuarmos os testes e mostrar o exemplo dos nomes listados dentro de uma tabela aninhada, e mostrará como se fosse uma matriz mesmo, com um índice para o acesso.
EXEMPLO 01:
SET SERVEROUTPUT ON;
DECLARE
 TYPE TABLE_NAMES IS TABLE OF VARCHAR2(30);
 vNome TABLE_NAMES := TABLE_NAMES('LEANDRO MIOZZO BONATO', 'JOÃO DA SILVA SAURO', 'FULANO', 'BELTRANO'); 
BEGIN
 DBMS_OUTPUT.PUT_LINE('NOME É: ' || vNome(1));
END;
RESULTADO 01:
bloco anônimo concluído
NOME É: LEANDRO MIOZZO BONATO
A variável “vNome” recebe uma serie de nomes de uma tabela aninhada TABLE_NAMES e o comando acima apenas imprimiu a qual é o nome posicionado no índice 1, lembrando que os índices em tabelas aninhadas se iniciam sempre na posição 1 e nunca em 0. Se executarmos este mesmo comando, porém substituindo a sua posição de 1 para 0, ocorrerá o seguinte erro: 
RESULTADO 02: 
ORA-06532: Subscrito além do limite
Isto porque o limite de quantidade de valores não pode ser menor que 1. Mas há outro tipo de erro de índices quando utilizamos tabelas aninhadas, que é quando o índice ultrapassa a quantidade de elementos disponíveis na tabela, e ocorre o seguinte erro: 
RESULTADO 03:
ORA-06533: Subscrito acima da contagem
Mas quando o comando DELETE, para deletamos um dos elementos, também não possuiremos acesso ao mesmo, pois o registro não será encontrado, ocorrerá a famosa exceção de NO_DATA_FOUND. Abaixo o comando executado seguido de seu resultado:
EXEMPLO 02:
SET SERVEROUTPUT ON;
DECLARE
 TYPE TABLE_NAMES IS TABLE OF VARCHAR2(30);
 vNome TABLE_NAMES := TABLE_NAMES('LEANDRO MIOZZO BONATO', 'JOÃO DA SILVA SAURO', 'FULANO', 'BELTRANO'); 
BEGIN
 vNome.DELETE(1);
 DBMS_OUTPUT.PUT_LINE('NOME É: ' || vNome(1));
END;
RESULTADO 04:
ORA-01403: dados não encontrados
	Nos próximos exemplos, implantaremos os nossos exemplos com as funções de repetição.
EXEMPLO 03:
SET SERVEROUTPUT ON;
DECLARE
 TYPE TABLE_NAMES IS TABLE OF VARCHAR2(30);
 vNome TABLE_NAMES := TABLE_NAMES('LEANDRO MIOZZO BONATO','JOÃO DA SILVA SAURO', 'FULANO','BELTRANO'); 
 i integer := vNome.FIRST;
BEGIN
 WHILE i IS NOT NULL LOOP
 DBMS_OUTPUT.PUT_LINE('NOME É: ' || vNome(I));
 I := vNome.NEXT(I);
 END LOOP; 
END;
RESULTADO 05:
bloco anônimo concluído
NOME É: LEANDRO MIOZZO BONATO
NOME É: JOÃO DA SILVA SAURO
NOME É: FULANO
NOME É: BELTRANO
	No caso acima, executamos apenas todos os elementos contidos na tabela aninhada, observe que precisamos criar uma variável para receber a posição do índice, atribuir a ela a primeira posição, e utilizá-la para percorrer por toda a tabela, também precisamos “sustenta-la”, ou atribuir novamente um valor para o seu índice dentro do WHILE, para que ela possa prosseguir com a leitura dos elementos. No exemplo utilizamos o comando FIRST, para buscar a primeira posição e o comando NEXT, para buscara próxima posição da tabela aninhada.
	Mas se por exemplo, um destes elementos durante a repetição deixasse de existir, ou fosse deletado, então o erro de dados não encontrados (NO_DATA_FOUND) apareceria para nos assombrar. Com isso precisamos sempre nos ater a criar um tratamento para caso este tipo de situação venha surgir, abaixo o exemplo com o tratamento: 
EXEMPLO 04: 
SET SERVEROUTPUT ON;
DECLARE
 TYPE TABLE_NAMES IS TABLE OF VARCHAR2(30);
 vNome TABLE_NAMES := TABLE_NAMES('LEANDRO MIOZZO BONATO','JOÃO DA SILVA SAURO', 'FULANO','BELTRANO'); 
 i integer := vNome.FIRST;
BEGIN
 vNome.delete(i);
 WHILE i IS NOT NULL LOOP
 IF vNome.EXISTS(I) THEN
 DBMS_OUTPUT.PUT_LINE('NOME É: ' || vNome(I));
 I := vNome.NEXT(I);
 ELSE 
 	DBMS_OUTPUT.PUT_LINE('NÃO EXISTE A POSIÇÃO ' || I);
 I := vNome.NEXT(I);
 END IF;
 END LOOP; 
END;
RESULTADO 06:
bloco anônimo concluído
NÃO EXISTE A POSIÇÃO 1
NOME É: JOÃO DA SILVA SAURO
NOME É: FULANO
NOME É: BELTRANO
É possível também utilizar o comando TRIM, que efetua a exclusão da quantidade de elementos em seu parâmetro de forma decrescente, por exemplo, a tabela aninhada possuí 10 posições e você resolve utilizar o comando TRIM para 5 elementos, então apenas sobrará os 5 primeiros elementos e os últimos 5 elementos serão apagados da tabela aninhada.
EXEMPLO 05: 
SET SERVEROUTPUT ON;
DECLARE
 TYPE TABLE_NAMES IS TABLE OF VARCHAR2(30);
 vNome TABLE_NAMES := TABLE_NAMES('LEANDRO MIOZZO BONATO','JOÃO DA SILVA SAURO', 'FULANO','BELTRANO'); 
 i integer := vNome.FIRST;
BEGIN
 vNome.TRIM(3);
 WHILE i IS NOT NULL LOOP
 DBMS_OUTPUT.PUT_LINE('NOME É: ' || vNome(I)); 
 i := vNome.NEXT(I); 
 END LOOP;
END;
RESULTADO 07: 
bloco anônimo concluído
NOME É: LEANDRO MIOZZO BONATO
	Ao contrário do comando DELETE, o comando TRIM, elimina a quantidade de elementos contidos em seu parâmetro de entrada, no exemplo acima, tínhamos 4 elementos utilizamos o comando TRIM e sobraram apenas 1 elemento, sendo apenas o primeiro elemento.
	O comando COUNT em uma NESTED TABLE, nada mais é do que o comando LAST, apenas na exceção de algum dos elementos ser deletados durante a execução do script. (MOORE, 2014)
EXEMPLO 06: 
SET SERVEROUTPUT ON;
DECLARE
 TYPE TABLE_NAMES IS TABLE OF VARCHAR2(30);
 vNome TABLE_NAMES := TABLE_NAMES('LEANDRO MIOZZO BONATO','JOÃO DA SILVA SAURO', 'FULANO','BELTRANO'); 
 i integer := vNome.FIRST;
BEGIN 
 DBMS_OUTPUT.PUT_LINE('Quantidade antes: ' || vNome.COUNT); 
 DBMS_OUTPUT.PUT_LINE('Último elemento antes: ' || vNome.LAST); 
 vNome.DELETE(1); 
 DBMS_OUTPUT.PUT_LINE('Quantidade depois: ' || vNome.COUNT); 
 DBMS_OUTPUT.PUT_LINE('Último elemento depois: ' || vNome.LAST); 
END;
RESULTADO 08:
bloco anônimo concluído
Quantidade antes: 4
Último elemento antes: 4
Quantidade depois: 3
Último elemento depois: 4
	No caso das tabelas aninhadas podemos também criar uma coleção de TYPES, definindo e declarando variáveis para posteriormente poder acessá-las e efetuar as transações no banco de dados necessárias. 
	O exemplo abaixo, mostra que deve-se declarar a variável com seu respectivo nome o seu tipo, se é tabela aninhada, VARRAY ou uma matriz associativa e então identificar qual o tipo do dado que irá ser utilizado (VARCHAR2, NUMBER, DATE, etc.), também no exemplo, deixamos os valores “fictícios” e partimos para algo que podemos utilizar na vida real, como uma tabela aninhada recebendo os valores de uma tabela com um cursor.
EXEMPLO 07:
SET SERVEROUTPUT ON;
DECLARE 
 CURSOR curNomLst is SELECT FIRST_NAME FROM EMPLOYEES WHERE ROWNUM <= 10; 
 TYPE tNomLst IS TABLE of EMPLOYEES.FIRST_NAME%TYPE; 
 refNomLst tNomLst := tNomLst(); 
 vCount INTEGER :=0; 
BEGIN 
 FOR N IN curNomLst LOOP 
 vCount := vCount +1; 
 refNomLst.extend; 
 refNomLst(vCount) := N.FIRST_NAME; 
 DBMS_OUTPUT.PUT_LINE('EMPLOYEE FIRST_NAME('||VCOUNT||'):'||REFNOMLST(VCOUNT)); 
 END LOOP; 
END;
RESULTADO 09: 
bloco anônimo concluído
EMPLOYEE FIRST_NAME(1):Ellen
EMPLOYEE FIRST_NAME(2):Sundar
EMPLOYEE FIRST_NAME(3):Mozhe
EMPLOYEE FIRST_NAME(4):David
EMPLOYEE FIRST_NAME(5):Hermann
EMPLOYEE FIRST_NAME(6):Shelli
EMPLOYEE FIRST_NAME(7):Amit
EMPLOYEE FIRST_NAME(8):Elizabeth
EMPLOYEE FIRST_NAME(9):Sarah
EMPLOYEE FIRST_NAME(10):David
No exemplo acima, é executado um cursor de uma consulta na tabela EMPLOYEES (funcionários) para o campo de FIRST_NAME (primeiro nome), apenas limitamos a quantidade de registros para 10 para utilizarmos em nosso exemplo, então em seguida definimos uma variável do tipo tabela com um campo do tipo da coluna FIRST_NAME da tabela EMPLOYEES (seria o tipo VARCHAR2, em resumo). Adiante definimos uma variável do tipo da nossa tabela criada anteriormente, recebendo os valores de nossa tabela, e em seguida definimos uma variável contadora do tipo inteiro.
Para atribuirmos os valores do cursor à nossa tabela aninhada e em seguida poder acessar os dados, devemos criar um LOOP onde defino como N tendo os registros do cursor CURNOMLST, então para que seja atribuído o valor à nossa tabela aninhada, propriamente dito, deve-se estender o tamanho da tabela para que caiba os registros, pois a mesma não possuí nenhum elemento e está vazia. Em seguida é atribuído o valor do cursor em um elemento no respectivo índice da variável VCOUNT.
	Também podemos atribuir à uma NESTED TABLE mais de uma coluna no cursor, que o Oracle irá interpretar normalmente. Conforme o exemplo abaixo, queremos agora saber qual é o primeiro nome, o último nome e o salário do funcionário, e com isso mostrar em nossa consulta como é simples transformar um campo de tabela aninhada em vários campos.
EXEMPLO 08: 
SET SERVEROUTPUT ON;
DECLARE 
 CURSOR curNomLst is SELECT FIRST_NAME, last_name, SALARY FROM EMPLOYEES WHERE ROWNUM <= 10; 
 TYPE tNomLst IS TABLE of EMPLOYEES%ROWTYPE; 
 refNomLst tNomLst := tNomLst(); 
 vCount INTEGER :=0; 
BEGIN 
 FOR N IN curNomLst LOOP 
 vCount := vCount +1; 
 refNomLst.extend; 
 refNomLst(vCount).first_name := N.FIRST_NAME; 
 refNomLst(vCount).LAST_name := N.LAST_NAME;
 refNomLst(vCount).SALARY := N.SALARY;
 DBMS_OUTPUT.PUT_LINE('EMPLOYEE FIRST_NAME('||VCOUNT||'):'||REFNOMLST(VCOUNT).FIRST_NAME 
 || ' - LAST_NAME: ' || REFNOMLST(VCOUNT).LAST_NAME 
 || ' - SALARY: ' || REFNOMLST(VCOUNT).SALARY); 
 END LOOP; 
END;
RESULTADO: 10
bloco anônimo concluído
EMPLOYEE FIRST_NAME(1):Steven - LAST_NAME: King - SALARY: 15000
EMPLOYEE FIRST_NAME(2):Neena - LAST_NAME: Kochhar - SALARY: 17000
EMPLOYEE FIRST_NAME(3):Lex - LAST_NAME: De Haan - SALARY: 17000
EMPLOYEE FIRST_NAME(4):Alexander - LAST_NAME: Hunold - SALARY: 9000
EMPLOYEE FIRST_NAME(5):Bruce - LAST_NAME: Ernst - SALARY: 6000
EMPLOYEE FIRST_NAME(6):David - LAST_NAME: Austin - SALARY: 4800
EMPLOYEE FIRST_NAME(7):Valli - LAST_NAME: Pataballa - SALARY: 4800
EMPLOYEE FIRST_NAME(8):Diana - LAST_NAME: Lorentz - SALARY: 4200
EMPLOYEE FIRST_NAME(9):Nancy - LAST_NAME: Greenberg - SALARY: 12008
EMPLOYEE FIRST_NAME(10):Daniel - LAST_NAME: Faviet - SALARY: 9000	
	No exemplo acima, adicionamos as colunas no nosso SELECT normalmente, apenas mudamos a referência da tabela aninhada que era feita antes diretamente para a coluna de FIRST_NAME para um ROWTYPE, que nada mais é do que a referência da linha toda. Consequentemente com isso tivemos que acrescentar na referência das colunas da tabela aninhada, os campos que receberão os valores, e também uma grande mudança no momento de referencia-los é acrescentar após o índice da tabela aninhada o nome do campo, ao contrário de quando se tem apenas uma coluna não há a necessidade de referencia-lo após o índice.
	Contando as NESTED TABLES possuem recursos mais poderosos do que apenas programação “pura” em PL/SQL, elas permitem se criar colunas em tabelas físicas, em resumo, é uma tabela de dados inteira, com suas respectivas propriedades, dentro de apenas uma coluna de tabela física. Abaixo os exemplos mostram como poderá ser utilizadotal recurso, e também como poderemos manipular os dados dentro desta coluna/tabela.
EXEMPLO 09:
CREATE TYPE EMPLOYEES_ELEMENTS AS OBJECT (
 EMPLOYEE_ID NUMBER(8,0), 
 FIRST_NAME VARCHAR2(20), 
 LAST_NAME VARCHAR2(25), 
 EMAIL VARCHAR2(25), 
 PHONE_NUMBER VARCHAR2(20), 
 HIRE_DATE DATE, 
 JOB_ID2 VARCHAR2(10), 
 SALARY NUMBER(8,2), 
 COMMISSION_PCT NUMBER(2,2), 
 MANAGER_ID NUMBER(6), 
 DEPARTMENT_ID NUMBER(4)
);
CREATE TYPE EMPLOYEES_TABLE AS TABLE OF EMPLOYEES_ELEMENTS;
CREATE TABLE EMPLOYEE_COPY (
 SEQ_EMPLOYEE NUMBER(8,0) NOT NULL ,
 EMPLOYEE_INFO EMPLOYEES_TABLE, 
 CONSTRAINT EMPLOYEE_COPY_PK PRIMARY KEY (SEQ_EMPLOYEE) ENABLE 
 )
NESTED TABLE EMPLOYEE_INFO STORE AS EMPLOYEES_INFOS;
CREATE SEQUENCE SEQ_EMPLOYEE_COPY INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999 MINVALUE 1 NOCACHE ORDER;
RESULTADO 11:
TYPE EMPLOYEES_ELEMENTS compilado
TYPE EMPLOYEES_TABLE compilado
table EMPLOYEE_COPY criado.
sequence SEQ_EMPLOYEE_COPY criado.
	No exemplo acima, primeiramente criamos um TYPE de variável do tipo OBJECT, que criará o objeto da tabela aninhada com todas as colunas necessárias, para este exemplo, estarei replicando a tabela EMPLOYEES, com todos os tipos de dados e nomes de campos. Em um segundo momento, é efetuada a criação de um novo TYPE de variável do tipo TABLE do TYPE criado anteriormente (EMPLOYEES_ELEMENTS), este TYPE estará referenciando e buscando todas as informações de propriedades criados no primeiro TYPE. Então, para finalizar, criamos uma tabela cópia (EMPLOYEE_COPY) da tabela de EMPLOYEES, porém criamos um campo da sequência e referenciamos apenas a coluna do nosso TYPE (EMPLOYEES_TABLE), em seguida “dizemos” para o Oracle que esta tabela possuí uma NESTED TABLE e o campo que está recebendo tal recurso é o EMPLOYEE_INFO e será armazenado os dados no TYPE EMPLOYEES_INFOS. Criamos também uma sequência para nos auxiliar no momento de inserir os registros da tabela, para a primeira coluna SEQ_EMPLOYEE, chamada de SEQ_EMPLOYEE_COPY.
	Para entendermos melhor o que estamos fazendo, vamos ver na prática como estará funcionando, segue o exemplo abaixo, onde iremos inserir alguns registros, para efetuar tal operação fizemos uma pequena automação para o comando INSERT.
EXEMPLO 10: 
DECLARE 
 CURSOR curEmployees is SELECT * FROM EMPLOYEES; 
BEGIN 
 FOR N IN curEmployees LOOP 
 INSERT INTO EMPLOYEE_COPY (SEQ_EMPLOYEE, EMPLOYEE_INFO)
 VALUES (SEQ_EMPLOYEE_COPY.NEXTVAL, EMPLOYEES_TABLE( EMPLOYEES_ELEMENTS ( N.EMPLOYEE_ID, N.FIRST_NAME, N.LAST_NAME, N.EMAIL, N.PHONE_NUMBER, N.HIRE_DATE, N.JOB_ID2, N.SALARY, N.COMMISSION_PCT, N.MANAGER_ID, N.DEPARTMENT_ID)));
 END LOOP; 
END;
Se efetuarmos uma consulta comum na tabela de EMPLOYEES_COPY, os registros que retornarão serão tudo (com exceção do SEQ_EMPLOYEE) em uma coluna de um tipo de dado de NESTED TABLE, segue abaixo o resultado da consulta comum na tabela em questão, o exemplo abaixo está limitado a 5 registros apenas, para que o mesmo não se estenda muito, mas o correto são 107 registros.
EXEMPLO 11: 
SELECT * FROM EMPLOYEE_COPY;
RESULTADO 12:
SEQ_EMPLOYEE EMPLOYEE_INFO 
------------ --------------------------------------------------
 1 HR.EMPLOYEES_TABLE(HR.EMPLOYEES_ELEMENTS(100,'Steven','King','SKING','515.123.4567','2003-06-17 00:00:00.0','AD_PRES',15000,NULL,100,90)) 
 2 HR.EMPLOYEES_TABLE(HR.EMPLOYEES_ELEMENTS(101,'Neena','Kochhar','NKOCHHAR','515.123.4568','2005-09-21 00:00:00.0','AD_VP',17000,NULL,100,90))3 HR.EMPLOYEES_TABLE(HR.EMPLOYEES_ELEMENTS(102,'Lex','De Haan','LDEHAAN','515.123.4569','2001-01-13 00:00:00.0','AD_VP',17000,NULL,100,90)) 
 4 HR.EMPLOYEES_TABLE(HR.EMPLOYEES_ELEMENTS(103,'Alexander','Hunold','AHUNOLD','590.423.4567','2006-01-03 00:00:00.0','IT_PROG',9000,NULL,102,60)) 
 5 HR.EMPLOYEES_TABLE(HR.EMPLOYEES_ELEMENTS(104,'Bruce','Ernst','BERNST','590.423.4568','2007-05-21 00:00:00.0','IT_PROG',6000,NULL,103,60)) 
	Para selecionar apenas um campo, ou apenas os campos desejados de uma NESTED TABLE pelo comando SELECT,	 deve-se apenas inserir na clausula do FROM a tabela que contém o campo, neste caso a EMPLOYEE_COPY, e então declarar uma nova tabela com o comando TABLE e indicar qual é a coluna NESTED TABLE da tabela referenciada da EMPLOYEE_COPY. Recomendo sempre criar “Alias” para as colunas referenciadas, para que fique mais fácil de poder acessá-las depois. Vejamos no exemplo abaixo como ficará a consulta no banco de dados, selecionando apenas alguns campos do objeto, o resultado é apenas o 5 primeiros registros. 
EXEMPLO 12:
SELECT B.FIRST_NAME, B.LAST_NAME 
 FROM EMPLOYEE_COPY A, TABLE (A.EMPLOYEE_INFO) B;
RESULTADO 13:
FIRST_NAME LAST_NAME 
-------------------- -------------------------
Tayler Fox 
Pat Fay 
Mattea Marvins 
Nandita Sarchand 
Martha Sullivan 
Também pode-se utilizar um comando de consulta normalmente para a coluna, onde poderá ser utilizado os comandos no WHERE para efetuar filtros, de forma em que se utiliza a mesma lógica do que para um SELECT comum, porém deve-se acrescentar o campo que deseja comparar, o tipo da variável de tabela e a tabela referenciada, um pouco mais complicado, mas funcional da mesma forma. O exemplo abaixo mostra com detalhes como poderá ser feita esta consulta, o resultado é apenas o 5 primeiros registros.
EXEMPLO 13: 
SELECT B.FIRST_NAME, B.LAST_NAME 
 FROM EMPLOYEE_COPY A, TABLE (A.EMPLOYEE_INFO) B
 WHERE B.SALARY >= 10000
RESULTADO 14: 
FIRST_NAME LAST_NAME 
-------------------- -------------------------
Pat Fay 
Nancy Greenberg 
Neena Kochhar 
Lex De Haan 
Harrison Bloom 
Podemos, além do comando INSERT, utilizar outros comandos de manipulação em SQL (DML), como por exemplo, o UPDATE e o DELETE, de forma normal e como de forma como os conhecemos, sem segredo algum. O exemplo abaixo mostra como pode-se ser executado o comando de atualização de registros.
EXEMPLO 14:
UPDATE TABLE(SELECT A.EMPLOYEE_INFO
 FROM EMPLOYEE_COPY A, TABLE (A.EMPLOYEE_INFO) B
 WHERE B.EMPLOYEE_ID = 100
 ) B 
 SET B.FIRST_NAME = 'LEANDRO BONATO'
	
EXEMPLO 15:
SELECT B.FIRST_NAME
 FROM EMPLOYEE_COPY A, TABLE (A.EMPLOYEE_INFO) B
 WHERE B.EMPLOYEE_ID = 100
RESULTADO 15:
1 linhas atualizado.
FIRST_NAME 
--------------------
LEANDRO BONATO 
EXEMPLO 16:
DELETE TABLE (SELECT A.EMPLOYEE_INFO
 FROM EMPLOYEE_COPY A 
 WHERE A.SEQ_EMPLOYEE = 2 
 ) A
 WHERE A.FIRST_NAME = 'LEANDRO BONATO'
RESULTADO 16: 
1 linhas deletado.
	Quanto ao desempenho das NESTED´s TABLE´s iremos testar da seguinte forma, primeiramente inserimos 1 000 000 (um milhão) de registros, utilizando o INSERT do exemplo 10, onde apenas iremos apenas acrecentar quantas vezes de 100 registros deverá ser inserido, no caso são 10 000 vezes que deverá ser repetido a instrução do exemplo 10, pois a tabela contém aproximadamente 100 registros. Então com isso também iremos inserir 1 000 000 de registros na tabela EMPLOYEES e verificar qual foi mais rápido para inserir os dados, e em seguida mostraremos a quantidade de tempo para listar e consultar estes registros no banco de dados, em cada e os compararemos. Abaixo está o exemplo de como deverá ficar cada um dos comandos de inserção. 
	
EXEMPLO 17: 
SET SERVEROUTPUT ON;
DECLARE 
 CURSOR curEmployees is SELECT * FROM EMPLOYEES; 
BEGIN 
 FOR I IN 1..10000 LOOP
 FOR N IN curEmployeesLOOP 
 INSERT INTO EMPLOYEE_COPY (SEQ_EMPLOYEE, EMPLOYEE_INFO)
 VALUES (SEQ_EMPLOYEE_COPY.NEXTVAL, 
 EMPLOYEES_TABLE( 
 EMPLOYEES_ELEMENTS(N.EMPLOYEE_ID, N.FIRST_NAME, 
 N.LAST_NAME, N.EMAIL, N.PHONE_NUMBER, 
 N.HIRE_DATE, N.JOB_ID2, N.SALARY,
 N.COMMISSION_PCT, N.MANAGER_ID, 
 N.DEPARTMENT_ID
 )
 )
 );
 END LOOP; 
 END LOOP;
END;
EXEMPLO 18: 
SELECT COUNT(1) FROM EMPLOYEE_COPY
RESULTADO 17:
bloco anônimo concluído.
Tarefa concluída em 612,671 segundos.
1070107 Registros
	Para o próximo exemplo faremos praticamente o mesmo INSERT, onde as mudanças serão na consulta que deverá ser EMPLOYEE_ID <= 206, para buscar apenas os mesmos que tinham na tabela EMPLOYEE_COPY, e criar uma variável contadora, já que não possuímos uma sequência para utilizar do comando NEXTVAL. Também deve-se alterar algumas propriedades da tabela, no campo EMPLOYEE_ID deverá ser aumentado o seu limite para no mínimo 7 (eu coloquei 8 por boas práticas) e desativar a chave única do campo email (EMP_EMAIL_UK). 
EXEMPLO 19: 
SET SERVEROUTPUT ON;
DECLARE 
 CURSOR curEmployees is SELECT * FROM EMPLOYEES where EMPLOYEE_ID <= 206; 
 vCOUNT INTEGER := 207;
BEGIN 
 FOR I IN 1..10000 LOOP
 FOR N IN curEmployees LOOP 
 INSERT INTO EMPLOYEES
 VALUES (vCOUNT, N.FIRST_NAME, N.LAST_NAME, N.EMAIL, N.PHONE_NUMBER, 
 N.HIRE_DATE, N.JOB_ID2, N.SALARY, N.COMMISSION_PCT, N.MANAGER_ID, 
 N.DEPARTMENT_ID); 
 vCOUNT := vCOUNT + 1;
 END LOOP; 
 END LOOP;
END;
EXEMPLO 20:
SELECT COUNT(1) FROM EMPLOYEES;
RESULTADO 18:
bloco anônimo concluído.
Tarefa concluída em 272,359 segundos.
1070107 Registros
	A ideia era fazer um comparativo entre dois INSERT´s, um normal entre a tabela de forma comum como conhecemos, e outro com a NESTED TABLE, com uma quantidade aproximada de registros, com isso teríamos um comparativo entre ambos para ver qual se comporta melhor. 
Vimos que no resultado as duas formas de inserção são muito parecidas em seu tempo de execução, testaremos agora uma consulta com aproximadamente 10% a 20% das tabelas para termos o tempo entre as duas e então mostrarei as vantagens e desvantagens de cada uma em cada situação.
EXEMPLO 21: 
SELECT A.EMPLOYEE_ID, A.FIRST_NAME, A.LAST_NAME, A.SALARY FROM EMPLOYEE_COPY A, TABLE(EMPLOYEE_INFO) B
WHERE A.SALARY >= 10000;
EXEMPLO 22: 
SELECT COUNT(1) FROM EMPLOYEE_COPY WHERE SALARY >= 10000;
RESULTADO 19:
200000 EXTRAÍDAS EM 10 SEGUNDOS
	
Faremos agora a mesma consulta apenas trocamos as tabelas para vermos quanto tempo demora e quantos registros retornam. 
EXEMPLO 23:
SELECT A.EMPLOYEE_ID, A.FIRST_NAME, A.LAST_NAME, A.SALARY FROM EMPLOYEES A
WHERE A.SALARY >= 10000;
EXEMPLO 24:
SELECT COUNT(1) FROM EMPLOYEES WHERE SALARY >= 10000;
RESULTADO 20: 
200020 LINHAS EXTRAÍDAS E, 12,063 SEGUNDOS
	Teoricamente a consulta na NESTED TABLE é mais rápida, mas vimos que um comando de INSERT é muito mais demorado, praticamente 225% de diferença entre uma tabela simples e uma tabela aninhada. Para comprovarmos a diferença de cada um iremos agora executar o plano de explicação e o rastreamento automático de cada uma das consultas.
RESULTADO 21: (NESTED TABLE)
RESULTADO 22: (TABELA FÍSICA)
	Reparamos que NESTED TABLE há muito mais cardinalidade de registros do que na tabela física, isto porque, todos os registros devem ser lidos e interpretados pela tabela aninhada e posteriormente interpretados pela tabela física
	A desvantagem de uma NESTED TABLE é que não podemos criar um índice na coluna de EMPLOYEE_INFO, até porque não faria sentido, pois se fosse indexada as colunas, seriam todas as colunas pertencentes a um índice, pois todas as colunas da tabela EMPLOYEE estão dentro da NESTED TABLE e isso não seria “uma boa prática”. Mas, se alterarmos a tabela EMPLOYEES e acrescentar na coluna SALARY um índice, o desempenho de nossa consulta não irá melhorar (devido à quantidade de registros), mas observem no exemplo que a mesma agora está utilizando um novo índice criado e utilizando outras formas de efetuar a própria consulta, incluindo as da própria chave primária. 
EXEMPLO 25: 
CREATE INDEX EMP_ARTIGO_IDX ON EMPLOYEES (SALARY ASC); 
RESULTADO 23: 
	Isto conclui-se que as NESTED TABLES quando se trata de operações mais comuns não se comportam muito bem, pois podem se tornar mais trabalhosa para se criar e também mais lentas no momento de retornar os dados. Mas são muito uteis quando utilizadas em PACKAGES, por exemplo, pois em uma PACKAGE o desenvolvedor poderá inserir vários tipos de variáveis e trabalhar com o PL/SQL sem problemas. Isto poderá trazer uma série de benefícios, como uma organização e algo mais próximo da para orientação à objetos, pois também não uma forma densa de programação, ou seja, o desenvolvedor pode optar por algo dinâmico, ao contrário dos VARRAY´s.
	No próximo artigo, será o último desta série de artigos sobre registros e coleções no ORACLE, onde mostrarei mais exemplos sobre o último tipo de matrizes chamado de Tabelas Associativas ou tabela de índice (Associative array or index-by-table).
REFERÊNCIAS
PRICE, Jason. ORACLE DATABASE 11G SQL: DOMINE SQL E PL/SQL NO BANCO DE DADOS ORACLE. 2009.
MOORE, Sheila. ORACLE® DATABASE PL/SQL LANGUAGE REFERENCE 11g RELEASE 1 (11.1) – B28370-05. 2009.
_____________. ORACLE® DATABASE PL/SQL LANGUAGE REFERENCE 11g RELEASE 2 (11.2) – E25519-13. 2014.
BURLESON, Don. ORACLE VARRAY EXAMPLES. 2008.
AGRADECIMENTOS
FABIO PELICER
ALEXANDRE PIETROBELLI
Abraços.

Continue navegando