Evolução das consultas hierárquicas do Oracle 9i ao 10g

O Oracle fornece algumas extensões úteis ao ANSI SQL para manipulação dos dados hierárquicos representados em uma tabela relacional.

Alguns aplicativos utilizam dados hierárquicos. Como exemplo temos aqueles que necessitam manipular organogramas, lista de materiais de uma fábrica de montagem ou árvores genealógicas. Esses tipos de informação são representados de forma mais conveniente em uma estrutura de árvore e, apesar do modelo relacional não ter sido elaborado para esse tipo de informação, eles podem ser facilmente organizados em uma tabela relacional por meio do uso de uma relação de autorreferência.

O Oracle fornece algumas extensões úteis ao ANSI SQL para manipulação dos dados hierárquicos representados em uma tabela relacional. Com as extensões fornecidas, você pode construir operações hierárquicas complexas em dados estruturados em árvore. Este artigo faz uma introdução aos dados hierárquicos abordando a criação de consultas no Oracle 9i e 10g.

Como os dados são cadastrados?

Para analisarmos o funcionamento de relações de autorreferência, será utilizado inicialmente como exemplo uma tabela para cadastrar localidades de um país (estados, cidades e bairros). Perceba que um país é composto de vários estados e estes estados são compostos de várias cidades e estas cidades possuem muitos bairros, ou seja, um exemplo claro de hierarquia. Veja o código para criação da tabela na c 1.

CREATE TABLE LOCALIDADES ( COD_LOCAL NUMBER(5), COD_LOCAL_PAI NUMBER(5), NOM_LOCAL VARCHAR2(30), PRIMARY KEY (COD_LOCAL), FOREIGN KEY (COD_LOCAL_PAI) REFERENCES LOCALIDADES (COD_LOCAL) );
Listagem 1

No código acima, o campo Cod_Local_Pai é chave estrangeira do campo Cod_Local da mesma tabela. O auto-relacionamento está definido. Em uma tabela de auto-relacionamento, no primeiro nível de uma hierarquia, o campo que se relaciona com a chave primária fica com o valor vazio. No nosso exemplo, esse campo é COD_LOCAL_PAI. Veja na Figura 1 um exemplo de como ficaria a organização dos locais, sendo o Brasil o 1º nível da hierarquia. Perceba também que a hierarquia possui cinco níveis. A Listagem 2 apresenta o código necessário para inserção destes dados na tabela Localidades.

Figura 1
INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL) VALUES (1, ‘Brasil’); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (2, ‘Rio de Janeiro’, 1); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (3, ‘Rio de Janeiro’, 2); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (4, ‘Jacarepaguá’, 3); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (5, ‘Madureira’, 3); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (6, ‘Taquara’, 4); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (7, ‘Freguesia’, 4); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (8, 'São Paulo', 1); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (9, 'Paraná', 1); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (10, 'Curitiba', 9); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (11, 'Água Verde', 10); INSERT INTO LOCALIDADES (COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI) VALUES (12, 'Batel', 10);
Listagem 2

Percebam que ao cadastrar o Brasil o código pai não foi informado pois ele está no primeiro nível da hierarquia.

Consultando os Dados

O Oracle 9i possui duas cláusulas que permitem uma leitura completa dos dados de uma tabela com auto-relacionamento:

A Listagem 3 apresenta o código para recuperar os dados da tabela Localidades de acordo com a hierarquia presente no auto-relacionamento. O resultado da consulta pode ser visualizada na Figura 2.

SELECT COD_LOCAL, NOM_LOCAL, COD_LOCAL_PAI FROM LOCALIDADES CONNECT BY PRIOR COD_LOCAL = COD_LOCAL_PAI START WITH COD_LOCAL_PAI IS NULL;
Listagem 3.
Figura 2

No exemplo, informamos na cláusula Start With que a pesquisa deverá ser iniciada pelos registros onde o código pai da localidade é nulo (no nosso caso o único existente é o Brasil). Na cláusula Connect By, informamos que o auto-relacionamento se dá com o COD_LOCAL igual a COD_LOCAL_PAI. Neste caso, a palavra reservada prior indica que o campo COD_LOCAL deverá ser procurado no nível ancestral da hierarquia e comparado com o valor de COD_LOCAL_PAI do registro atual.

Assim, o percurso seguido pela pesquisa começa no nível definido no comando Start With e é percorrido linha a linha, onde em cada nível os nós são lidos da esquerda para a direita. Deste modo, os passos seguidos na execução da consulta da Listagem 3 são:

  1. É verificada no 2º nível da hierarquia a existência de, pelo menos, um nó;
  2. Existindo nós, a pesquisa começa pelo nó mais à esquerda;
  3. Os dados do nó são retornados e se o mesmo tiver ramificações, os dados dos nós posteriores são retornados obedecendo à ordem de pesquisa que é da esquerda para a direita;
  4. Ao terminar a pesquisa do nó mais à esquerda, serão retornados os dados do segundo nó do 2º nível;
  5. A pesquisa termina ao chegar no último nó da árvore hierárquica.
Nota: É importante o leitor atentar para o uso da palavra PRIOR. Seu uso não é obrigatório, mas o efeito de não usá-lo é bastante diferente. Isto por que a cláusula PRIOR garante a ligação com a linha anterior da hierarquia. Sem o seu uso, apenas o primeiro elemento da hierarquia é recuperado. Por exemplo, para a consulta apresentada na listagem 3 sem o PRIOR, teríamos como resultado apenas o nó Brasil.

A Pseudocoluna Level

Ao utilizar a cláusula Connect By em um comando SQL, é possível verificar o nível hierárquico do registro listado. Para isto temos a coluna Level, exemplificada na Listagem 4 e na Figura 3.

SELECT LEVEL, COD_LOCAL, NOM_LOCAL FROM LOCALIDADES CONNECT BY PRIOR COD_LOCAL = COD_LOCAL_PAI START WITH COD_LOCAL_PAI IS NULL;
Listagem 4
Figure 3

O uso desta coluna é importante, por exemplo, quando quisermos filtrar o resultado da pesquisa. A Listagem 5 apresenta uma consulta onde são retornados apenas os registros do terceiro nível da hierarquia, ou seja, as cidades. O resultado da consulta pode ser visto na Figura 4.

SELECT COD_LOCAL, NOM_LOCAL FROM LOCALIDADES WHERE LEVEL = 3 CONNECT BY PRIOR COD_LOCAL = COD_LOCAL_PAI START WITH COD_LOCAL_PAI IS NULL;
Listagem 5
Figura 4

Endentando o Resultado da Pesquisa

Para uma melhor visualização da árvore hierárquica, podemos utilizar a função RPAD para endentar os registros de acordo com seu nível. O RPAD é uma função que possui 2 parâmetros, o texto que será retornado pela função e o espaçamento da endentação. A Listagem 6 apresenta um exemplo de uso da endentação e a Figura 5, seu resultado.

SELECT RPAD(‘ ’, LEVEL*5 – 5) || NOM_LOCAL AS LOCAL FROM LOCALIDADES CONNECT BY PRIOR COD_LOCAL = COD_LOCAL_PAI START WITH COD_LOCAL_PAI IS NULL;
Listagem 6
Figura 5

Diferenças entre o Oracle 8i e Oracle 9i

SELECT NOM_LOCAL, SYS_CONNECT_BY_PATH(NOM_LOCAL, ‘\’) AS CAMINHO FROM LOCALIDADES CONNECT BY PRIOR COD_LOCAL = COD_LOCAL_PAI START WITH COD_LOCAL_PAI IS NULL;
Listagem 8
Figura 7

Vimos até aqui uma introdução às consultas hierárquicas e como o tratamento fornecido a elas evoluiu do Oracle 8i para o 9i. A partir de agora discutiremos novos avanços acrescentados na versão 10g do Oracle.

Melhorias na Consulta Hierárquica do Oracle 10g

A partir de agora utilizaremos a tabela EMPLOYEE para apresentar as novidades que o Oracle 10g traz no tratamento às consultas hierárquicas. A Listagem 9 apresenta o comando para criação da tabela.

CREATE TABLE EMPLOYEE ( EMP_ID NUMBER (4) CONSTRAINT EMP_PK PRIMARY KEY, EMP_NAME VARCHAR2 (15) NOT NULL, DEPT_ID NUMBER (2) NOT NULL, MGR_ID NUMBER (4) CONSTRAINT EMP_FK REFERENCES EMPLOYEE(EMP_ID), SALARY NUMBER (7,2) NOT NULL, HIRE_DATE DATE NOT NULL);
Listagem 9

Observe novamente a limitação da chave externa (foreign key constraint) EMP_FK na coluna MGR_ID, que faz referência à coluna EMP_ID da mesma tabela. Nesta tabela, MGR_ID representa a EMP_ID do gerente de um funcionário.

Como vimos, até a versão 9i, as extensões hierárquicas do Oracle incluíam a cláusula START WITH … CONNECT BY e a pseudocoluna LEVEL. Ainda utilizando os recursos presentes no Oracle 9i, o exemplo da Listagem 10 mostra os funcionários por ordem hierárquica e utiliza um recuo de linha para posicionar os subordinados abaixo deles.

SELECT LEVEL, LPAD(' ',2*(LEVEL - 1)) || EMP_NAME "EMPLOYEE", EMP_ID, MGR_ID FROM EMPLOYEE START WITH MGR_ID IS NULL CONNECT BY PRIOR EMP_ID = MGR_ID; LEVEL EMPLOYEE EMP_ID MGR_ID ---------- -------------------- ---------- ---------- 1 KING 7839 2 JONES 7566 7839 3 SCOTT 7788 7566 4 ADAMS 7876 7788 3 FORD 7902 7566 4 SMITH 7369 7902 2 BLAKE 7698 7839 3 ALLEN 7499 7698 3 WARD 7521 7698 3 MARTIN 7654 7698 3 TURNER 7844 7698 3 JAMES 7900 7698 2 CLARK 7782 7839 3 MILLER 7934 7782
Listagem 10

Os novos recursos de consulta hierárquica do Oracle 10g são:

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados