2.6. Junções entre tabelas #

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:

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.