Até agora, nossas consultas acessaram apenas uma tabela por vez.
As consultas podem acessar várias tabelas ao mesmo tempo, ou acessar
a mesma tabela de forma que várias linhas da tabela sejam processadas
ao mesmo tempo. As consultas que acessam várias tabelas (ou várias
instâncias da mesma tabela) de uma vez, são chamadas de consultas de
junção. Elas combinam linhas de uma tabela com
linhas de uma segunda tabela, com uma expressão especificando quais
linhas devem ser juntadas. Por exemplo, para retornar todos os registros
meteorológicos junto com a localização da cidade associada, o banco
de dados precisa comparar a coluna cidade
de cada linha da tabela clima com a coluna
nome de todas as linhas da tabela
cidades e selecionar os pares de linhas
onde esses valores correspondem.
[11]
Esta operação pode ser efetuada por meio da seguinte consulta:
SELECT *
FROM clima JOIN cidades ON cidade = nome;
cidade | temp_min | temp_max | prcp | data | nome | localizacao
---------------+----------+----------+------+------------+---------------+-----------------------
Salvador - BA | 21 | 28 | 3.4 | 2022-06-25 | Salvador - BA | (-38.48806,-13.01477)
Salvador - BA | 23 | 27 | 0 | 2022-06-26 | Salvador - BA | (-38.48806,-13.01477)
(2 linhas)
Duas observações sobre o resultado:
Não existe linha para a cidade
Rio de Janeiro - RJ no resultado produzido.
Isso acontece por não haver entrada correspondente na tabela
cidades para
Rio de Janeiro - RJ, e a junção ignora as linhas
que não têm correspondência na tabela clima.
Veremos em breve como isso pode ser corrigido.
Existem duas colunas contendo o nome da cidade, o que está correto,
porque a lista de colunas das tabelas clima
e cidades estão concatenadas. Na prática
isso não é desejado, sendo preferível, portanto, escrever a lista
das colunas de saída explicitamente em vez de utilizar o
*:
SELECT cidade, temp_min, temp_max, prcp,
data, localizacao
FROM clima JOIN cidades ON cidade = nome;
Como todas as colunas possuem nomes diferentes, o analisador encontra automaticamente a tabela que a coluna pertence. Se existissem nomes de colunas duplicados nas duas tabelas, seria necessário qualificar os nomes das colunas para indicar qual delas está sendo referenciada, como em:
SELECT clima.cidade, clima.temp_min,
clima.temp_max, clima.prcp,
clima.data, cidades.localizacao
FROM clima JOIN cidades ON cidade = nome;
Qualificar todos os nomes de colunas nas consultas de junção é frequentemente considerado uma boa prática, para que a consulta não falhe ao se adicionar posteriormente um nome de coluna duplicado a uma das tabelas.
As consultas de junção do tipo visto até agora também podem ser escritas da seguinte forma alternativa:
SELECT *
FROM clima, cidades
WHERE cidade = nome;
Esta sintaxe é anterior à sintaxe
JOIN/ON,
introduzida no SQL-92.
As tabelas são simplesmente listadas na cláusula
FROM,
e a expressão de comparação é adicionada à cláusula
WHERE.
Os resultados dessa sintaxe implícita mais antiga e da sintaxe explícita
JOIN/ON mais recente são idênticos.
Mas para quem lê a consulta, a sintaxe explícita torna seu significado
mais fácil de entender: A condição de junção é introduzida por sua
própria palavra-chave, enquanto anteriormente a condição era misturada
na cláusula WHERE junto com outras condições.
Agora vamos descobrir como se faz para obter as linhas de
Rio de Janeiro - RJ. Desejamos o seguinte: que a
consulta examine a tabela clima e, para cada
uma de suas linhas, encontre a linha correspondente na tabela
cidades. Se não for encontrada nenhuma linha
correspondente, desejamos que sejam colocados
“valores vazios” (nulos) nas colunas da tabela
cidades. Esse tipo de consulta é chamada de
junção externa (outer join).
As junções vistas até agora foram junções internas
(inner join). O comando então fica assim:
SELECT *
FROM clima
LEFT OUTER JOIN cidades
ON (clima.cidade = cidades.nome);
cidade | temp_min | temp_max | prcp | data | nome | localizacao
---------------------+----------+----------+------+------------+---------------+-----------------------
Salvador - BA | 21 | 28 | 3.4 | 2022-06-25 | Salvador - BA | (-38.48806,-13.01477)
Salvador - BA | 23 | 27 | 0 | 2022-06-26 | Salvador - BA | (-38.48806,-13.01477)
Rio de Janeiro - RJ | 17 | 29 | | 2022-06-29 | |
(3 linhas)
Esta consulta é chamada de junção externa esquerda (left outer join), porque a tabela mencionada à esquerda do operador de junção terá cada uma de suas linhas mostrada na saída pelo menos uma vez, enquanto a tabela à direita terá somente as linhas correspondendo a alguma linha da tabela à esquerda mostrada na saída. Ao listar uma linha da tabela à esquerda, para a qual não existe nenhuma linha correspondente na tabela à direita, são colocados valores vazios (nulos) nas colunas da tabela à direita.
Exercício: Existem também a junção externa direita (right outer join) e a junção externa completa (full outer join). Tente descobrir o que fazem.
Também é possível fazer a junção da tabela consigo mesma.
Isso é chamado de autojunção
(self join).
Como exemplo, suponha que desejamos descobrir todas as linhas de
clima que estão no intervalo de temperatura de outros registros de
clima. Para isso é necessário comparar as colunas
temp_min e temp_max
de cada linha da tabela clima com as colunas
temp_min e temp_max
de todas as outras linhas da mesma tabela clima,
o que pode ser feito utilizando a seguinte consulta:
SELECT C1.cidade, C1.temp_min AS menor, C1.temp_max AS maior,
C2.cidade, C2.temp_min AS menor, C2.temp_max AS maior
FROM clima C1, clima C2
WHERE C1.temp_min < C2.temp_min
AND C1.temp_max > C2.temp_max;
cidade | menor | maior | cidade | menor | maior
---------------------+-------+-------+---------------+-------+-------
Salvador - BA | 21 | 28 | Salvador - BA | 23 | 27
Rio de Janeiro - RJ | 17 | 29 | Salvador - BA | 21 | 28
Rio de Janeiro - RJ | 17 | 29 | Salvador - BA | 23 | 27
(3 linhas)
A tabela clima teve seu nome mudado para C1 e
C2, para ser possível distinguir o lado esquerdo
e o lado direito da junção. Esses tipos de “aliases”
também podem ser utilizados em outras consultas para reduzir a
digitação como, por exemplo:
SELECT *
FROM clima w, cidades c
WHERE w.cidade = c.nome;
Será encontrada essa forma de abreviar com bastante frequência.
[11] Esse é apenas um modelo conceitual, a junção é geralmente feita de uma maneira mais eficiente que comparar cada par de linhas possível, mas isso não é visível para o usuário.