Clique aqui para ler esse artigo em PDF.imagem_pdf.jpg

capaSQL12.JPG

Clique aqui para ler todos os artigos desta edição

Consultas com Outer Join no Oracle

 

Uma das dúvidas mais freqüentes, especialmente para quem está começando a utilizar a linguagem SQL, refere-se à criação de consultas com o operador de outer join quando existem condições de seleção sobre as tabelas. Neste artigo, discutiremos questões relativas à sintaxe e ao modo de utilização do outer join. Faremos também uma comparação de algumas diferenças existentes entre o outer join definido na ANSI e no Oracle.

 

Nota: os exemplos em SQL ANSI foram testados no Oracle 9i e no MySQL 4.0. Já os exemplos que utilizam a sintaxe da Oracle foram testados no Oracle 8i e 9i. Se você utiliza outro banco de dados, verifique se ele suporta a sintaxe ANSI do operador outer join, já que a maioria dos bancos de dados atuais não implementa a especificação do SQL ANSI em sua totalidade. Por exemplo, a sintaxe ANSI para o operador outer join não é suportada no Oracle 8i.

 

Para demonstrar as operações exemplificadas neste artigo, analisaremos as tabelas DEPARTAMENTO, PESSOA e HOBBY, cujos dados são apresentados nas Tabelas 1, 2 e 3, respectivamente. Resumidamente, estas tabelas nos informam que uma pessoa pode estar alocada em um departamento e ter vários hobbies.

 

COD_DEPTO

NOME_DEPTO

RH

Recursos Humanos

FIN

Financeiro

ADM

Administração

COM

Comercial

Tabela 1 - Estrutura e dados da tabela Departamento.

 

ID

NOME

UF

COD_DEPTO

100

Antônio

RJ

FIN

200

Pedro

AM

ADM

300

Ana

DF

COM

400

Marcos

MG

COM

500

Elisa

DF

ADM

600

Márcia

PB

ADM

700

Patrícia

DF

FIN

800

Carlos

SP

 

Tabela 2 - Estrutura e dados da tabela Pessoa.

 

ID_PESSOA

NOME_HOBBY

100

Futebol

200

Natação

300

Futebol

400

Patinação

500

Volley

600

Cinema

700

Cinema

Tabela 3 - Estrutura e dados da tabela Hobby.

 

Operação de Outer Join

A operação de outer join é usada em comandos SELECT do SQL para combinar linhas de duas ou mais tabelas, como, por exemplo, duas tabelas A e B. Para utilizar a operação de outer join, é necessário definir uma delas como principal (A) e a outra secundária (B). O resultado de outer join é o conjunto de todas as linhas da tabela principal A acrescidas das linhas de B, segundo a regra: caso exista uma ou mais linhas de B que atendam à condição de join, será feita a junção dessas linhas com a linha de A. Caso contrário, o operador juntará a linha de A com uma linha montada a partir das colunas de B preenchidas com nulos.

Para facilitar o entendimento, vamos exemplificar primeiro um inner join entre as tabelas PESSOA e HOBBY. A Listagem 1 apresenta a consulta que retorna apenas as pessoas que possuem algum hobby, bem como a descrição destes.

 

Listagem 1 - Inner join simples entre as tabelas Pessoa e Hobby.

 

select p.id, p.nome, h.nome_hobby

from pessoa p,

  hobby h

where p.id = h.id_pessoa;

ID

NOME

NOME_HOBBY

100

Antônio

Futebol

100

Antônio

Natação

200

Pedro

Futebol

300

Ana Maria

Patinação

400

Marcos

Volley

500

Elisa

Cinema

700

Patrícia

Cinema

 

Já para listarmos todas as pessoas no resultado, incluindo as que não têm hobby, devemos usar outer join no lugar do inner join. A sintaxe desta operação será diferente no SQL ANSI e no SQL da Oracle. A sintaxe para o ANSI é apresentada na Listagem 2.

 

Nota: a diferença entre outer join e inner join está no fato de que, na primeira, todas as linhas da tabela principal aparecem no resultado.

 

Listagem 2 - Sintaxe ANSI da outer join.

 

SELECT colunas

FROM tabela_primaria LEFT OUTER JOIN

  tabela_secundaria ON

  (tabela_primaria.coluna =

  tabela_secundaria.coluna)

 

A sintaxe de outer join da Oracle (ver Listagem 3) acrescenta o operador (+) na condição de join (cláusula where) ao lado do nome das colunas da tabela escolhida como secundária.

 

Listagem 3 - Sintaxe do outer join da Oracle.

 

SELECT colunas

FROM tabela_primaria, tabela_secundaria

WHERE (tabela_primaria.coluna =

   tabela_secundaria.coluna(+))

 

A Listagem 4 apresenta as consultas no formato ANSI e, em seguida, no formato Oracle. Estas fazem um outer join entre as tabelas PESSOA e HOBBY. Nestas consultas, a tabela PESSOA é definida como principal, uma vez que o resultado deverá apresentar todas as pessoas registradas, independentemente de elas terem algum hobby associado.

 

Listagem 4

 

Outer join entre as tabelas Pessoa e Hobby.

-- Sintaxe ANSI

select p.id, p.nome, h.nome_hobby

from pessoa p LEFT OUTER JOIN

  hobby h ON (h.id_pessoa = p.id);

 

-- Sintaxe Oracle

select p.id, p.nome, h.nome_hobby

from pessoa p,

  hobby h

where p.id = h.id_pessoa (+);

 

ID

NOME

NOME_HOBBY

100

Antônio

Futebol

100

Antônio

Natação

200

Pedro

Futebol

300

Ana Maria

Patinação

400

Marcos

Volley

500

Elisa

Cinema

600

Márcia

Cinema

700

Patrícia

 

800

Carlos

 

 

Escrevendo condições de seleção em consultas com Outer Join

É possível escrever condições de seleção nas colunas de tabelas definidas como secundárias em uma consulta de outer join. Essas condições podem ser especificadas de forma que o filtro seja imposto: (1) sobre as linhas da tabela secundária (antes da operação de outer join) ou (2) sobre o conjun to de linhas resultante da operação de outer join (após o outer join). Os exemplos a seguir demonstram as duas abordagens.

As consultas descritas na Listagem 5 relacionam todos os departamentos, apresentando as pessoas alocadas a cada um deles. Neste caso, a tabela DEPARTAMENTO foi definida como principal, e a tabela PESSOA, como secundária.

 

Listagem 5 - Outer join entre as tabelas Departamento e Pessoa.

 

-- Sintaxe ANSI

select d.cod_depto, d.nome_depto, p.id, p.nome

from departamento d LEFT OUTER JOIN pessoa p on

  ( d.cod_depto = p.cod_depto);

 

-- Sintaxe Oracle

select d.cod_depto, d.nome_depto, p.id, p.nome

from    departamento d, pessoa p

where  d.cod_depto = p.cod_depto (+);

 

COD_DEPTO

NOME_DEPTO

ID

NOME

ADM

Administração

200

Pedro

ADM

Administração

500

Elisa

ADM

Administração

600

Márcia

COM

Comercial

300

Ana Maria

COM

Comercial

400

Marcos

FIN

Financeiro

100

Antônio

FIN

Financeiro

700

Patrícia

RH

Recursos Humanos

 

 

 

Agora vamos impor uma condição sobre as linhas da tabela PESSOA. Faremos uma consulta que apresente todos os departamentos que possuam pelo menos uma pessoa de determinada UF. Para isso, incluímos a condição p.uf = ‘DF’ na cláusula where das consultas da Listagem 5. A Listagem 6 mostra as consultas alteradas e seu resultado.

 

Listagem 6 - Outer join entre as tabelas departamento e pessoa com condicional no where.

 

-- Sintaxe ANSI

select d.cod_depto, d.nome_depto, p.id, p.nome, p.uf

from departamento d LEFT OUTER JOIN pessoa p on

  (d.cod_depto = p.cod_depto)

where p.uf = ‘DF’;

 

-- Sintaxe Oracle

select d.cod_depto, d.nome_depto, p.id, p.nome, p.uf

from   departamento d, pessoa p

where d.cod_depto = p.cod_depto (+)

 and p.uf = ‘DF’;

 

COD_DEPTO

NOME_DEPTO

ID

NOME

UF

ADM

Administração

500

Elisa

DF

COM

Comercial

300

Ana Maria

DF

FIN

Financeiro

700

Patrícia

DF

 

Da maneira como foi escrita, a condição filtra as linhas do conjunto resultante do outer join. Para impor um filtro sobre as linhas da tabela antes da operação de outer join, é preciso alterar o comando SQL. Na forma ANSI, para que uma condição seja imposta antes da operação de outer join, é preciso defini-la na cláusula ON (ver Listagem 7). Já na sintaxe do Oracle é necessário sinalizar com o operador (+) de outer join ao lado do nome da coluna usada na condição de seleção (ver Listagem 7).

 

Listagem 7 - Outer join entre as tabelas Departamento e Pessoa com condicional no outer join.

 

-- Sintaxe ANSI

select d.cod_depto, d.nome_depto, p.id, p.nome, p.uf

from departamento d left outer join pessoa p on

  (d.cod_depto = p.cod_depto and p.uf = ‘DF’);

 

-- Sintaxe Oracle

select d.cod_depto, d.nome_depto, p.id, p.nome, p.uf

from    departamento d, pessoa p

where d.cod_depto = p.cod_depto (+)

and     p.uf(+) = ‘DF’;

 

COD_DEPTO

NOME_DEPTO

ID

NOME

UF

ADM

Administração

500

Elisa

DF

COM

Comercial

300

Ana Maria

DF

FIN

Financeiro

700

Patrícia

DF

RH

Recursos Humanos

 

 

 

 

Na Listagem 7 todos os departamentos foram apresentados e apenas as pessoas do DF foram consideradas. Da forma como foram escritas, as condições impõem um filtro sobre a tabela PESSOA e selecionam apenas as linhas que poderão participar da operação de outer join.

 

Otimizando a execução do NOT IN com outer join

A possibilidade de filtrar linhas da tabela secundária ou linhas do conjunto resultante da operação de outer join nos permite realizar operações complexas de modo eficiente. Veremos isto na prática comparando os resultados de uma consulta com NOT IN e uma busca que retorne os mesmos resultados utilizando, porém, o outer join. Para isto, vejamos a seguinte consulta: Que pessoas não têm futebol como hobby? A Listagem 8 apresenta a versão sem a utilização do outer join.

 

Listagem 8 - Uso do not in para listar pessoas que não têm Futebol como Hobby.

 

select p.id, p.nome

from    pessoa p

where  p.id not in

(select id_pessoa

from    hobby

where  nome_hobby = ‘Futebol’);

 

No entanto, é necessário ter cuidado, pois essa construção requer a leitura de todas as linhas da tabela das queries principal (PESSOA) e secundária (HOBBY), o que pode fazer com que a consulta fique lenta se as tabelas tiverem um volume grande de dados. Uma forma mais eficiente de fazer essa pesquisa é utilizar um outer join com a condicional no outer join e filtrar as colunas resultantes de valor null (ver Listagem 9). Note que as consultas das Listagens 8 e 9 mostram o mesmo resultado exibido na Listagem 10.

 

Listagem 9 - Uso do outer join para listar pessoas que não têm Futebol como Hobby.

 

-- Sintaxe ANSI

select p.id, p.nome

from      pessoa p left outer join hobby h on

(p.id = h.id_pessoa and h.nome_hobby = ‘Futebol’)

where    h.id_pessoa is null;

 

-- Sintaxe Oracle

select p.id, p.nome

from  pessoa p,

hobby h

where   p.id = h.id_pessoa (+) and

h.nome_hobby (+) = ‘Futebol’ and

h.id_pessoa is null;

 

Listagem 10 - Resultado dos comandos das listagens 8 e 9.

 

ID

NOME

300

Ana Maria

400

Marcos

500

Elisa

600

Márcia

700

Patrícia

800

Carlos

 

Para esclarecer por que as consultas da Listagem 9 fornecem os mesmos resultados da Listagem 8, vamos examinar um dos passos intermediários de sua construção, retirando temporariamente a condição de filtragem do campo null (ver Listagem 11).

 

Listagem 11 - Consultas e resultado da execução sem o “filtro null”.

 

-- Sintaxe ANSI

select p.id, p.nome, h.id_pessoa, h.nome_hobby

from   pessoa p left outer join hobby h on

(p.id = h.id_pessoa and h.nome_hobby = ‘Futebol’);

 

-- Sintaxe Oracle

select p.id, p.nome, h.id_pessoa, h.nome_hobby

from      pessoa p,

hobby h

where   p.id = h.id_pessoa (+) and

h.nome_hobby (+) = ‘Futebol’;

 

ID

NOME

ID_PESSOA

NOME_HOBBY

100

Antônio

100

Futebol

200

Pedro

200

Futebol

300

Ana Maria

 

 

400

Marcos

 

 

500

Elisa

 

 

600

Márcia

 

 

700

Patrícia

 

 

800

Carlos

 

 

 

Observe na Listagem 11 que as condições on (p.id=h.id_pessoa and h.nome_hobby=’Futebol’), no SQL ANSI, e h.nome_hobby (+) = ‘Futebol’, no Oracle, filtram as linhas da tabela hobby antes da operação de outer join ser executada. Isto permite que apenas as linhas que contêm o hobby ‘Futebol’ participem do join. Nessa operação, poderão ser retornados os valores correspondentes para as colunas ID_PESSOA e NOME_HOBBY como valores nulos (caso a pessoa não tenha futebol como hobby). Então, para eliminar do conjunto resultante do outer join apenas aqueles que gostem de futebol, é utilizada a condição h.id_pessoa is null. Assim, as consultas da Listagem 9 implementam, através do outer join, a mesma lógica correspondente a um operador NOT IN, porém com um desempenho melhor, já que não precisam acessar todas as linhas das tabelas principal e secundária. A Listagem 12 apresenta o plano de execução das duas abordagens e mostra com mais clareza o ganho de desempenho.

 

Listagem 12 - Plano de execução das consultas com NOT IN e outer join.

 

--Consulta com NOT IN. Acesso full nas duas tabelas.

OPERATION

------------------------------

OPTIONS

-------------

OBJECT_NAME

----------------------

SELECT STATEMENT

FILTER

TABLE ACCESS

TABLE ACCESS

<null>

<null>

FULL

FULL

<null>

<null>

PESSOA

HOBBY

 

--Consulta com outer join. Acesso com índice no final do processo.

OPERATION

------------------------------

OPTIONS

-------------

OBJECT_NAME

----------------------

SELECT STATEMENT

FILTER

NESTED LOOPS

TABLE ACCESS

INDEX

 

(5 Linhas Selecionadas)

<null>

<null>

OUTER

FULL

UNIQUE SCAN

<null>

<null>

<null>

PESSOA

SYS_C0040202

 

Conclusão

Este artigo apresentou, por meio de exemplos simplificados, a sintaxe e o modo de utilização da operação de outer join no ANSI e no Oracle. Mostrou também a utilidade de escrever condições de seleção associadas a colunas de tabelas secundárias de um outer join, e como fazê-lo. Por fim, apresentou uma solução eficiente para implementar o operador NOT IN através de um outer join. Em sistemas reais, onde é mais comum lidarmos com consultas complexas, poderemos nos deparar com situações em que essas técnicas serão bastante úteis.

 

 

José Eduardo Thurler Tecles (tecles@formatool.com.br) é formado em Engenharia Eletrônica pelo Instituto Militar de Engenharia - IME, RJ, em 1976 e fez mestrado na Universidade de Maryland, EUA, em 1980. Atua há anos como analista de sistemas e é consultor da Formato Projetos e Desenvolvimento de Sistemas, especializado no desenvolvimento de sistemas corporativos e Data Marts/Data Warehouse. É também instrutor homologado pela Oracle; já ministrou cursos de SQL, PL/SQL, projeto de Data Marts, modelagem multidimensional e projeto de banco de dados para Data Warehousing / Business Intelligence.

 

Anne Margareth Souza Guedes (anne.margareth@formatool.com.br) é formada em Ciência da Computação pelo Centro de Ensino Unificado de Brasília - CEUB, DF, em 1993. Atua como DBA há 9 anos e é consultora da Formato Projetos e Desenvolvimento de Sistemas. Obteve sua certificação Oracle em 1999 e possui agora as certificações Oracle Certified Professional (OCP) DBA 9i, 8i e 8.0.