Introdução
A divisão de dados relacionados em tabelas separadas pode ser benéfica do ponto de vista de consistência, flexibilidade e certos tipos de desempenho. No entanto, você ainda precisa de uma maneira razoável de reintegrar registros quando as informações relevantes abrangem várias tabelas.
Em bancos de dados relacionais, junções oferecem uma maneira de combinar os registros em duas ou mais tabelas com base em valores de campo comuns. Diferentes tipos de junções podem obter resultados diferentes dependendo de como as linhas sem correspondência devem ser tratadas. Neste guia, discutiremos os vários tipos de junções que o PostgreSQL oferece e como você pode usá-los para combinar dados de tabela de várias fontes.
O que são junções?
Resumindo, junções são uma maneira de exibir dados de várias tabelas. Eles fazem isso juntando registros de diferentes fontes com base em valores correspondentes em determinadas colunas. Cada linha resultante consiste em um registro da primeira tabela combinado com uma linha da segunda tabela, com base em uma ou mais colunas em cada tabela com o mesmo valor.
A sintaxe básica de uma junção é assim:
SELECT *FROM <first_table><join_type> <second_table> <join_condition>;
Em uma junção, cada linha resultante é construída incluindo todas as colunas da primeira tabela seguidas por todas as colunas da segunda tabela. O
SELECT
parte da consulta pode ser usada para especificar as colunas exatas que você deseja exibir. Várias linhas podem ser construídas a partir das tabelas originais se os valores nas colunas usadas para comparação não forem exclusivos. Por exemplo, imagine que você tem uma coluna sendo comparada da primeira tabela que possui dois registros com valor "vermelho". Combinada com isso, há uma coluna da segunda tabela que possui três linhas com esse valor. A junção produzirá seis linhas diferentes para esse valor representando as várias combinações que podem ser alcançadas.
O tipo de junção e as condições de junção determinam como cada linha exibida é construída. Isso afeta o que acontece com as linhas de cada tabela que não tem uma correspondência na condição de junção.
Por conveniência, muitas junções correspondem à chave primária em uma tabela com uma chave estrangeira associada na segunda tabela. Embora as chaves primárias e estrangeiras sejam usadas apenas pelo sistema de banco de dados para manter as garantias de consistência, seu relacionamento geralmente as torna boas candidatas para condições de junção.
Diferentes tipos de junções
Vários tipos de junções estão disponíveis, cada um dos quais potencialmente produzirá resultados diferentes. Entender como cada tipo é construído ajudará você a determinar qual é apropriado para diferentes cenários.
Junto interno
A junção padrão é chamada de junção interna . No PostgreSQL, isso pode ser especificado usando
INNER JOIN
ou simplesmente JOIN
. Aqui está um exemplo típico demonstrando a sintaxe de uma junção interna:
SELECT *FROM table_1[INNER] JOIN table_2 ON table_1.id = table_2.table_1_id;
Uma junção interna é o tipo mais restritivo de junção porque exibe apenas linhas criadas pela combinação de linhas de cada tabela. Quaisquer linhas nas tabelas constituintes que não tenham uma contraparte correspondente na outra tabela são removidas dos resultados. Por exemplo, se a primeira tabela tiver um valor "azul" na coluna de comparação e a segunda tabela não tiver nenhum registro com esse valor, essa linha será suprimida da saída.
Se você representar os resultados como um diagrama de Venn das tabelas de componentes, uma junção interna permitirá que você represente a área de sobreposição dos dois círculos. Nenhum dos valores que existiam apenas em uma das tabelas são exibidos.
Juntar à esquerda
Uma junção esquerda é uma junção que mostra todos os registros encontrados em uma junção interna, além de todos os não correspondentes linhas da primeira tabela. No PostgreSQL, isso pode ser especificado como um
LEFT OUTER JOIN
ou apenas como um LEFT JOIN
. A sintaxe básica de uma junção esquerda segue este padrão:
SELECT *FROM table_1LEFT JOIN table_2 ON table_1.id = table_2.table_1_id;
Uma junção esquerda é construída executando primeiro uma junção interna para construir linhas de todos os registros correspondentes em ambas as tabelas. Depois, os registros não correspondidos da primeira tabela também são incluídos. Como cada linha em uma junção inclui as colunas de ambas as tabelas, as colunas sem correspondência usam
NULL
como o valor para todas as colunas na segunda tabela. Se você representar os resultados como um diagrama de Venn das tabelas de componentes, uma junção à esquerda permite representar todo o círculo esquerdo. As partes do círculo esquerdo representadas pela interseção entre os dois círculos terão dados adicionais complementados pela tabela direita.
Juntar à direita
Uma junção à direita é uma junção que mostra todos os registros encontrados em uma junção interna, além de todos os não correspondentes linhas da segunda tabela. No PostgreSQL, isso pode ser especificado como um
RIGHT OUTER JOIN
ou apenas como um RIGHT JOIN
. A sintaxe básica de uma junção direita segue este padrão:
SELECT *FROM table_1RIGHT JOIN table_2 ON table_1.id = table_2.table_1_id;
Uma junção à direita é construída executando primeiro uma junção interna para construir linhas de todos os registros correspondentes em ambas as tabelas. Em seguida, os registros não correspondentes da segunda tabela também são incluídos. Como cada linha em uma junção inclui as colunas de ambas as tabelas, as colunas sem correspondência usam
NULL
como o valor para todas as colunas na primeira tabela. Se você representar os resultados como um diagrama de Venn das tabelas de componentes, uma junção à direita permite representar todo o círculo à direita. As partes do círculo direito representadas pela interseção entre os dois círculos terão dados adicionais complementados pela tabela da esquerda.
Participação completa
Uma junção completa é uma junção que mostra todos os registros encontrados em uma junção interna, além de todos os não correspondentes linhas de ambas as tabelas de componentes. No PostgreSQL, isso pode ser especificado como um
FULL OUTER JOIN
ou apenas como um FULL JOIN
. A sintaxe básica de uma junção completa segue este padrão:
SELECT *FROM table_1FULL JOIN table_2 ON table_1.id = table_2.table_1_id;
Uma junção completa é construída executando primeiro uma junção interna para construir linhas de todos os registros correspondentes em ambas as tabelas. Depois, os registros não correspondidos de ambas as tabelas também são incluídos. Como cada linha em uma junção inclui as colunas de ambas as tabelas, as colunas sem correspondência usam
NULL
como o valor de todas as colunas na outra tabela sem correspondência. Se você representar os resultados como um diagrama de Venn das tabelas de componentes, uma junção completa permitirá que você represente inteiramente os dois círculos de componentes. A interseção dos dois círculos terá valores fornecidos por cada uma das tabelas componentes. As partes dos círculos fora da área de sobreposição terão os valores da tabela a que pertencem, usando
NULL
para preencher as colunas encontradas na outra tabela. Juntar cruzado
Uma junção especial chamada
CROSS JOIN
também está disponível. Uma junção cruzada não usa nenhuma comparação para determinar se as linhas em cada tabela correspondem umas às outras. Em vez disso, os resultados são construídos simplesmente adicionando cada uma das linhas da primeira tabela a cada uma das linhas da segunda tabela. Isso produz um produto cartesiano das linhas em duas ou mais tabelas. Na verdade, esse estilo de junção combina as linhas de cada tabela incondicionalmente. Portanto, se cada tabela tiver três linhas, a tabela resultante terá nove linhas contendo todas as colunas de ambas as tabelas.
Por exemplo, se você tiver uma tabela chamada
t1
combinado com uma tabela chamada t2
, cada uma com linhas r1
, r2
e r3
, o resultado seria nove linhas combinadas assim:t1.r1 + t2.r1t1.r1 + t2.r2t1.r1 + t2.r3t1.r2 + t2.r1t1.r2 + t2.r2t1.r2 + t2.r3t1.r3 + t2.r1t1.r3 + t2.r2t1.r3 + t2.r3
Auto-junção
Uma autojunção é qualquer junção que combina as linhas de uma tabela consigo mesma. Pode não ser imediatamente aparente como isso pode ser útil, mas na verdade tem muitas aplicações comuns.
Muitas vezes, as tabelas descrevem entidades que podem cumprir várias funções em relação umas às outras. Por exemplo, se você tiver uma tabela de
people
, cada linha pode conter uma mother
coluna que faz referência a outras people
na mesa. Uma autojunção permitiria unir essas diferentes linhas juntando uma segunda instância da tabela à primeira em que esses valores correspondam. Como as autojunções fazem referência à mesma tabela duas vezes, os aliases de tabela são necessários para desambiguar as referências. No exemplo acima, por exemplo, você pode juntar as duas instâncias do
people
tabela usando os aliases people AS children
e people AS mothers
. Dessa forma, você pode especificar a qual instância da tabela você está se referindo ao definir as condições de junção. Aqui está outro exemplo, desta vez representando relacionamentos entre funcionários e gerentes:
SELECT *FROM people AS employeeJOIN people AS manager ON employee.manager_id = manager.id;
Condições de junção
Ao combinar tabelas, a condição de junção determina como as linhas serão combinadas para formar os resultados compostos. A premissa básica é definir as colunas em cada tabela que devem corresponder para que a junção ocorra nessa linha.
O ON
cláusula
A maneira mais padrão de definir as condições para junções de tabelas é com o
ON
cláusula. O ON
cláusula usa um sinal de igual para especificar as colunas exatas de cada tabela que serão comparadas para determinar quando uma junção pode ocorrer. O PostgreSQL usa as colunas fornecidas para unir as linhas de cada tabela. O
ON
cláusula é a mais detalhada, mas também a mais flexível das condições de junção disponíveis. Ele permite especificidade independentemente de quão padronizados sejam os nomes das colunas de cada tabela que está sendo combinada. A sintaxe básica do
ON
cláusula fica assim:SELECT *FROM table1JOIN table2ON table1.id = table2.ident;
Aqui, as linhas de
table1
e table2
será juntado sempre que o id
coluna de table1
corresponde ao ident
coluna de table2
. Como uma junção interna é usada, os resultados mostrarão apenas as linhas que foram unidas. Como a consulta usa o curinga *
caractere, todas as colunas de ambas as tabelas serão exibidas. Isso significa que tanto o
id
coluna de table1
e o ident
coluna de table2
serão exibidos, mesmo que tenham o mesmo valor exato em virtude de satisfazer a condição de junção. Você pode evitar essa duplicação chamando as colunas exatas que deseja exibir no SELECT
lista de colunas. O USING
cláusula
O
USING
cláusula é um atalho para especificar as condições de um ON
cláusula que pode ser usada quando as colunas que estão sendo comparadas têm o mesmo nome em ambas as tabelas. O USING
A cláusula recebe uma lista, entre parênteses, dos nomes das colunas compartilhadas que devem ser comparadas. A sintaxe geral do
USING
cláusula usa este formato:SELECT *FROM table1JOIN table2USING (id, state);
Esta junção combina
table1
com table2
quando duas colunas que ambas as tabelas compartilham (id
e state
) cada um tem valores correspondentes. Esta mesma junção pode ser expressa mais detalhadamente usando
ON
assim:SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state;
Embora ambas as junções acima resultem na construção das mesmas linhas com os mesmos dados presentes, elas seriam exibidas ligeiramente diferentes. Enquanto o
ON
cláusula inclui todas as colunas de ambas as tabelas, a USING
cláusula suprime as colunas duplicadas. Então, em vez de haver dois id
separados colunas e dois state
separados colunas (uma para cada tabela), os resultados teriam apenas uma de cada uma das colunas compartilhadas, seguidas por todas as outras colunas fornecidas por table1
e table2
. O NATURAL
cláusula
O
NATURAL
cláusula é mais uma abreviação que pode reduzir ainda mais a verbosidade do USING
cláusula. Um NATURAL
join não especifica qualquer colunas a serem correspondidas. Em vez disso, o PostgreSQL unirá automaticamente as tabelas com base em todas as colunas que possuem colunas correspondentes em cada banco de dados. A sintaxe geral do
NATURAL
cláusula join fica assim:SELECT *FROM table1NATURAL JOIN table2;
Supondo que
table1
e table2
ambos têm colunas chamadas id
, state
e company
, a consulta acima seria equivalente a esta consulta usando o ON
cláusula:SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state AND table1.company = table2.company;
E esta consulta usando o
USING
cláusula:SELECT *FROM table1JOIN table2USING (id, state, company);
Como o
USING
cláusula, o NATURAL
A cláusula suprime colunas duplicadas, portanto, haveria apenas uma única instância de cada uma das colunas unidas nos resultados. Enquanto o
NATURAL
cláusula pode reduzir a verbosidade de suas consultas, deve-se ter cuidado ao usá-la. Como as colunas usadas para unir as tabelas são calculadas automaticamente, se as colunas nas tabelas de componentes forem alteradas, os resultados poderão ser muito diferentes devido a novas condições de união. Condições de junção e WHERE
cláusula
As condições de junção compartilham muitas características com as comparações usadas para filtrar linhas de dados usando
WHERE
cláusulas. Ambas as construções definem expressões que devem ser avaliadas como verdadeiras para que a linha seja considerada. Por causa disso, nem sempre é intuitivo qual é a diferença entre incluir comparações adicionais em um WHERE
construir versus defini-los dentro da própria cláusula de junção. Para entender as diferenças que resultarão, temos que dar uma olhada na ordem em que o PostgreSQL processa as diferentes partes de uma consulta. Nesse caso, os predicados na condição de junção são processados primeiro para construir a tabela virtual unida na memória. Após este estágio, as expressões dentro do
WHERE
cláusula são avaliadas para filtrar as linhas resultantes. Como exemplo, suponha que temos duas tabelas chamadas
customer
e order
que precisamos nos unir. Queremos unir as duas tabelas combinando o customer.id
coluna com o order.customer_id
coluna. Além disso, estamos interessados nas linhas na order
tabela que tem um product_id
de 12345. Dados os requisitos acima, temos duas condições com as quais nos preocupamos. A maneira como expressamos essas condições, no entanto, determinará os resultados que recebemos.
Primeiro, vamos usar ambos como condições de junção para um
LEFT JOIN
:SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_id AND order.product_id = 12345;
Os resultados poderiam ser algo assim:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345 4380 | Acme Co | | 320 | Other Co | | 20 | Early Co | | 8033 | Big Co | |(7 rows)
O PostgreSQL chegou a esse resultado executando as seguintes operações:
- Combine todas as linhas no
customer
tabela com oorder
tabela onde:customer.id
corresponde aorder.customer_id
.order.product_id
corresponde a 12345
- Como estamos usando uma junção à esquerda, inclua qualquer sem correspondência linhas da tabela esquerda (
customer
), preenchendo as colunas da tabela direita (order
) comNULL
valores. - Exibe apenas as colunas listadas em
SELECT
especificação de coluna.
O resultado é que todas as nossas linhas unidas correspondem às duas condições que estamos procurando. No entanto, a junção à esquerda faz com que o PostgreSQL também inclua todas as linhas da primeira tabela que não satisfizeram a condição de junção. Isso resulta em linhas "sobradas" que parecem não seguir a intenção aparente da consulta.
Se movermos a segunda consulta (
order.product_id
=12345) para um WHERE
cláusula, em vez de incluí-la como uma condição de junção, obtemos resultados diferentes:SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_idWHERE order.product_id = 12345;
Desta vez, apenas três linhas são exibidas:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345(3 rows)
A ordem em que as comparações são executadas é o motivo dessas diferenças. Desta vez, o PostgreSQL processa a consulta assim:
- Combine todas as linhas no
customer
tabela com oorder
tabela ondecustomer.id
corresponde aorder.customer_id
. - Como estamos usando uma junção à esquerda, inclua qualquer sem correspondência linhas da tabela esquerda (
customer
), preenchendo as colunas da tabela direita (order
) comNULL
valores. - Avalie o
WHERE
cláusula para remover quaisquer linhas que não tenham 12345 como valor para oorder.product_id
coluna. - Exibe apenas as colunas listadas em
SELECT
especificação de coluna.
Desta vez, embora estejamos usando uma junção à esquerda, o
WHERE
cláusula trunca os resultados filtrando todas as linhas sem o product_id
correto . Como todas as linhas sem correspondência teriam product_id
definido como NULL
, isso remove todas as linhas sem correspondência que foram preenchidas pela junção esquerda. Ele também remove qualquer uma das linhas que foram correspondidas pela condição de junção que não passou nesta segunda rodada de verificações. Compreender o processo básico que o PostgreSQL usa para executar suas consultas pode ajudá-lo a evitar alguns erros fáceis de cometer, mas difíceis de depurar, enquanto você trabalha com seus dados.
Conclusão
Neste guia, abordamos como as junções permitem que os bancos de dados relacionais combinem dados de diferentes tabelas para fornecer respostas mais valiosas. Falamos sobre as várias junções que o PostgreSQL suporta, a maneira como cada tipo monta seus resultados e o que esperar ao usar tipos específicos de junções. Depois, examinamos diferentes maneiras de definir as condições de junção e analisamos como a interação entre as junções e o
WHERE
cláusula pode levar a surpresas. As junções são uma parte essencial do que torna os bancos de dados relacionais poderosos e flexíveis o suficiente para lidar com tantos tipos diferentes de consultas. Organizar dados usando limites lógicos e ainda ser capaz de recombinar os dados de novas maneiras caso a caso dá a bancos de dados relacionais como o PostgreSQL uma versatilidade incrível. Aprender como realizar essa junção entre tabelas permitirá que você crie consultas mais complexas e conte com o banco de dados para criar imagens completas de seus dados.