Join (SQL)

Origem: Wikipédia, a enciclopédia livre.

Uma cláusula join da SQL - correspondente a uma operação de junção em álgebra relacional - combina colunas de uma ou mais tabelas em um banco de dados relacional. Ela cria um conjunto que pode ser salvo como uma tabela ou usado da forma como está. Um JOIN é um meio de combinar colunas de uma (auto-junção) ou mais tabelas, usando valores comuns a cada uma delas. O SQL padrão ANSI especifica cinco tipos de JOIN: INNER, LEFT OUTER, RIGHT OUTER, FULL OUTER e CROSS. Como um caso especial, uma tabela (tabela base, visão ou tabela juntada) pode se juntar a si mesma em uma auto-união (self-join).

Um programador declara uma instrução JOIN para identificar linhas para junção. Se o predicado avaliado for verdadeiro, a linha combinada é, então, produzida no formato esperado, um conjunto de linhas ou uma tabela temporária.

Tabelas de exemplo[editar | editar código-fonte]

Os bancos de dados relacionais geralmente são normalizados para eliminar a duplicação de informações, como quando os tipos de entidade têm relacionamentos um-para-muitos. Por exemplo, um departamento pode estar associado a vários funcionários. A junção de tabelas separadas para departamento e empregado cria, efetivamente, outra tabela que combina as informações de ambas as tabelas.

Todas as explicações subsequentes sobre tipos de junção neste artigo utilizam as duas tabelas a seguir. As linhas nessas tabelas servem para ilustrar o efeito de diferentes tipos de junções e predicados de junção. A coluna IDDepartamento da tabela Departamento (que pode ser designada como Departamento.IDDepartamento) é a chave primária, enquanto Empregado.IDDepartamento é uma chave estrangeira.

Tabela Empregado
ÚltimoNome IDDepartamento
Rafferty 31
Jones 33
Heisenberg 33
Robinson 34
Smith 34
Williams NULO
Tabela Departamento
IDDepartamento NomeDepartamento
31 Vendas
33 Engenharia
34 Administrativo
35 Marketing

Observação: Na tabela Empregado acima, o empregado "Williams" ainda não foi atribuído a nenhum departamento. Além disso, observe que nenhum funcionário é atribuído ao departamento de "Marketing".

Esta é a instrução SQL para criar as tabelas acima mencionadas.

CREATE TABLE departamento
(
 IDDepartamento INT Primary key,
 NomeDepartamento VARCHAR(20)
);

CREATE TABLE empregado
(
 ÚltimoNome VARCHAR(20),
 IDDepartamento INT references departamento(IDDepartamento)
);

INSERT INTO departamento VALUES(31, 'Vendas');
INSERT INTO departamento VALUES(33, 'Engenharia');
INSERT INTO departamento VALUES(34, 'Administrativo');
INSERT INTO departamento VALUES(35, 'Marketing');

INSERT INTO empregado VALUES('Rafferty', 31);
INSERT INTO empregado VALUES('Jones', 33);
INSERT INTO empregado VALUES('Heisenberg', 33);
INSERT INTO empregado VALUES('Robinson', 34);
INSERT INTO empregado VALUES('Smith', 34);
INSERT INTO empregado VALUES('Williams', NULL);

Junção cruzada (cross join)[editar | editar código-fonte]

Uma junção cruzada, CROSS JOIN, retorna o produto cartesiano dos registros (linhas) das tabelas na junção. Em outras palavras, ela produzirá registros que combinam cada registro da primeira tabela com cada registro da segunda tabela.[1]

Exemplo de uma junção cruzada explícita:

SELECT *
FROM empregado CROSS JOIN departamento;

Exemplo de um cross join implícito:

SELECT *
FROM empregado, departamento;
Empregado.ÚltimoNome Empregado.IDDepartamento Departamento.NomeDepartamento Departamento.IDDepartamento
Rafferty 31 Venda 31
Jones 33 Venda 31
Heisenberg 33 Venda 31
Smith 34 Venda 31
Robinson 34 Venda 31
Williams NULO Venda 31
Rafferty 31 Engenharia 33
Jones 33 Engenharia 33
Heisenberg 33 Engenharia 33
Smith 34 Engenharia 33
Robinson 34 Engenharia 33
Williams NULO Engenharia 33
Rafferty 31 Administrativo 34
Jones 33 Administrativo 34
Heisenberg 33 Administrativo 34
Smith 34 Administrativo 34
Robinson 34 Administrativo 34
Williams NULO Administrativo 34
Rafferty 31 Marketing 35
Jones 33 Marketing 35
Heisenberg 33 Marketing 35
Smith 34 Marketing 35
Robinson 34 Marketing 35
Williams NULO Marketing 35

A junção cruzada não aplica nenhum predicado para filtrar linhas da tabela resultante da junção. Os resultados de uma junção cruzada podem ser filtrados usando uma cláusula WHERE, que pode então produzir o equivalente a uma junção interna.

No padrão SQL:2011, as junções cruzadas fazem parte do pacote opcional F401, "Extended joined table".

Usos comuns são para verificar o desempenho do servidor.

Junção interna (inner join)[editar | editar código-fonte]

A Venn Diagram showing the inner overlapping portion filled.
Um diagrama de Venn representando uma instrução SQL de uma junção interna entre as tabelas A e B.

Uma junção interna, INNER JOIN, requer que cada linha, nas duas tabelas da junção, tenha valores de coluna correspondentes, e é uma operação de junção comumente usada em aplicativos, mas não deve ser considerada a melhor escolha em todas as situações. A junção interna cria uma nova tabela de resultados combinando valores de coluna de duas tabelas (A e B) com base no predicado de junção. A consulta compara cada linha de A com cada linha de B para encontrar todos os pares de linhas que satisfazem o predicado da junção. Quando o predicado da junção é satisfeito pela correspondência de valores não-NULOS, os valores de coluna para cada par de linhas correspondentes de A e B são combinados em uma linha de resultado.

O resultado da junção pode ser definido como o resultado da primeira obtenção do produto cartesiano (ou Cross join) de todas as linhas nas tabelas (combinando todas as linhas da tabela A com todas as linhas da tabela B) e retornando todas as linhas que satisfazem o predicado da junção. Implementações SQL reais normalmente usam outras abordagens, como junções de hash ou junções de mesclagem de ordenação, pois o cálculo do produto cartesiano é mais lento e, geralmente, requer, proibitivamente, uma grande quantidade de memória para armazenar.

O SQL especifica duas formas sintáticas diferentes para expressar as junções: a "notação de junção explícita" e a "notação de junção implícita". A "notação de junção implícita" não é mais considerada uma prática recomendada, embora os sistemas de banco de dados ainda a suportem.

A "notação de junção explícita" usa a palavra-chave JOIN, opcionalmente precedida pela palavra-chave INNER, para especificar a tabela a ser unida e a palavra-chave ON para especificar os predicados para a junção, como no exemplo a seguir:

SELECT empregado.ÚltimoNome, empregado.IDDepartamento, departamento.NomeDepartamento 
FROM empregado
INNER JOIN departamento ON
empregado.IDDepartamento = departamento.IDDepartamento;
Empregado.ÚltimoNome Empregado.Departamento Departamento.NomeDepartamento
Robinson 34 Administrativo
Jones 33 Engenharia
Smith 34 Administrativo
Heisenberg 33 Engenharia
Rafferty 31 Vendas

A "notação de junção implícita" simplesmente lista as tabelas para unir, na cláusula FROM da instrução SELECT, usando vírgulas para separá-las. Assim, ele especifica uma junção cruzada, e a cláusula WHERE pode aplicar predicados de filtro adicionais (que funcionam comparativamente aos predicados da junção na notação explícita).

O exemplo a seguir é equivalente ao anterior, mas desta vez usando notação de junção implícita:

SELECT *
FROM empregado, departamento
WHERE empregado.IDDepartamento = departamento.IDDepartamento;

Junção externa (outer join)[editar | editar código-fonte]

A tabela resultante da junção retém cada linha - mesmo que não exista outra linha correspondente. As junções externas subdividem-se, adicionalmente, em junções externas esquerdas (left outer joins), junções externas direitas (right outer joins) e junções externas completas (full outer joins), dependendo de quais linhas da tabela são retidas: esquerda, direita ou ambas (neste caso, esquerda e direita referem-se aos dois lados da palavra-chave JOIN). Assim como as junções internas, pode-se ainda sub-categorizar todos os tipos de junções externas, como junções de igualdade, junções naturais, ON <predicado> (junção-θ ou θ-join), etc.[2]

Não existe nenhuma notação de junção implícita para junções externas no SQL padrão.

A Venn Diagram showing the left circle and overlapping portion filled.
Um diagrama de Venn representando a instrução SQL de Left Join, entre as tabelas A e B.

Junção externa esquerda (left outer join)[editar | editar código-fonte]

O resultado de uma junção externa esquerda (ou simplesmente junção esquerda - left join) para as tabelas A e B sempre contém todas as linhas da tabela "esquerda" (A), mesmo se a condição de junção não encontrar nenhuma linha correspondente na tabela "direita" (B). Isso significa que se a cláusula ON corresponder a 0 (zero) linhas em B (para uma determinada linha em A), a união ainda retornará uma linha no resultado (para aquela linha) - mas com NULO em cada coluna de B. Uma junção externa esquerda retorna todos os valores de uma junção interna mais todos os valores na tabela à esquerda que não correspondem à tabela da direita, incluindo linhas com valores NULO (vazios) na coluna de ligação.

Por exemplo, isso nos permite encontrar o departamento de um funcionário, mas ainda mostra funcionários que não foram atribuídos a um departamento (ao contrário do exemplo de associação interna acima, em que funcionários não atribuídos foram excluídos do resultado).

Exemplo de uma junção externa esquerda (a palavra-chave OUTER é opcional), com a linha de resultado adicional (comparada com a junção interna) em itálico:

SELECT *
FROM empregado
LEFT OUTER JOIN departamento ON empregado.IDDepartamento = departamento.IDDepartamento;
Empregado.ÚltimoNome Empregado.IDDepartamento Departamento.NomeDepartamento Departamento.IDDepartamento
Jones 33 Engenharia 33
Rafferty 31 Vendas 31
Robinson 34 Administrativo 34
Smith 34 Administrativo 34
Williams NULO NULO NULO
Heisenberg 33 Engenharia 33

Sintaxes alternativas[editar | editar código-fonte]

O Oracle suporta a sintaxe depreciada[3]:

SELECT *
FROM empregado, departamento
WHERE empregado.IDDepartamento = departamento.IDDepartamento(+)

O Sybase suporta a sintaxe (o Microsoft SQL Server depreciou essa sintaxe desde a versão 2000):

SELECT *
FROM empregado, departamento
WHERE empregado.IDDepartamento *= departamento.IDDepartamento

O IBM Informix suporta a sintaxe:

SELECT *
FROM empregado, OUTER departamento
WHERE empregado.IDDepartamento = departmento.IDDepartamento
A Venn Diagram show the right circle and overlapping portions filled.
Um diagrama de Venn representando a instrução de junção direita entre as tabelas A e B.

Junção externa direita (right outer join)[editar | editar código-fonte]

Uma junção externa direita (junção direita ou right join) se assemelha a uma junção externa esquerda, exceto com o tratamento das tabelas invertidas. Cada linha da tabela "direita" (B) aparecerá na tabela resultante da junção pelo menos uma vez. Se nenhuma linha correspondente da tabela "esquerda" (A) existir, aparecerá NULO nas colunas de A para aquelas linhas que não tiverem correspondência em B.

Uma junção externa direita retorna todos os valores da tabela da direita e os valores correspondentes da tabela da esquerda (NULO no caso de nenhum predicado de junção correspondente). Por exemplo, isso nos permite encontrar cada funcionário e seu departamento, mas ainda mostrar departamentos que não possuem funcionários.

Abaixo está um exemplo de uma junção externa direita (a palavra-chave OUTER é opcional), com a linha de resultado adicional em itálico:

SELECT *
FROM empregado RIGHT OUTER JOIN departamento
  ON empregado.IDDepartamento = departamento.IDDepartamento;
Empregado.ÚltimoNome Empregado.IDDepartamento Departamento.NomeDepartamento Departamento.IDDepartamento
Smith 34 Administrativo 34
Jones 33 Engenharia 33
Robinson 34 Administrativo 34
Heisenberg 33 Engenharia 33
Rafferty 31 Vendas 31
NULO NULO Marketing 35

Junções externas esquerda e direita são funcionalmente equivalentes. Nenhuma das duas fornece qualquer funcionalidade que a outra não tenha, portanto, as junções externas direita e esquerda podem substituir umas às outras, desde que a ordem da tabela seja trocada.

A Venn Diagram showing the right circle, left circle, and overlapping portion filled.
Um diagrama de Venn representando a instrução SQL de Junção Completa entre as tabelas A e B.

Junção externa completa (full outer join)[editar | editar código-fonte]

Conceitualmente, uma junção externa completa combina o efeito de aplicar junções externas esquerda e direita. Onde as linhas nas tabelas que forem unidas pela junção completa não corresponderem, o conjunto de resultados terá valores NULO para cada coluna da tabela que não possuir uma linha correspondente. Para aquelas linhas que corresponderem, uma única linha será produzida no conjunto de resultados (contendo colunas preenchidas de ambas as tabelas).

Por exemplo, isso nos permite ver cada funcionário que está em um departamento e cada departamento que tem um funcionário, mas também ver cada funcionário que não faz parte de um departamento e cada departamento que não tem um funcionário.

Exemplo de uma junção externa completa (a palavra-chave OUTER é opcional):

SELECT *
FROM empregado FULL OUTER JOIN departamento
  ON empregado.IDDepartamento = departamento.IDDepartamento;
Empregado.ÚltimoNome Empregado.IDDepartamento Departamento.NomeDepartamento Departamento.IDDepartamento
Smith 34 Administrativo 34
Jones 33 Engenharia 33
Robinson 34 Administrativo 34
Williams NULO NULO NULO
Heisenberg 33 Engenharia 33
Rafferty 31 Administrativo 31
NULO NULO Marketing 35


Alguns sistemas de banco de dados não suportam a funcionalidade de junção externa completa diretamente, mas podem emular através do uso de uma junção interna e de seleções UNION ALL das "linhas de tabela única" das tabelas esquerda e direita, respectivamente. O mesmo exemplo pode aparecer da seguinte maneira:

SELECT empregado.ÚltimoNome, empregado.IDDepartamento,
       departamento.NomeDepartamento, departamento.IDDepartamento
FROM empregado
INNER JOIN departamento ON empregado.IDDepartamento = departamento.IDDepartamento

UNION ALL

SELECT empregado.ÚltimoNome, empregado.IDDepartamento,
       cast(NULL as varchar(20)), cast(NULL as integer)
FROM empregado
WHERE NOT EXISTS (
    SELECT * FROM departamento
             WHERE empregado.IDDepartamento = departamento.IDDepartamento)

UNION ALL

SELECT cast(NULL as varchar(20)), cast(NULL as integer),
       departamento.NomeDepartamento, departamento.IDDepartamento
FROM departamento
WHERE NOT EXISTS (
    SELECT * FROM empregado
             WHERE empregado.IDDepartamento = departamento.IDDepartamento)

Referências

  1. SQL CROSS JOIN
  2. Silberschatz, Abraham; Korth, Hank; Sudarshan, S. (2002). Database System Concepts 4ª ed. [S.l.: s.n.] p. 166. ISBN 0072283637 
  3. Oracle Left Outer Join