Hasta ahora, nuestras consultas solo han accedido a una tabla a la vez.
Las consultas pueden acceder a múltiples tablas a la vez, o acceder a la misma
tabla de tal manera que se procesen varias filas de la tabla al mismo tiempo.
Las consultas que acceden a múltiples tablas (o a múltiples instancias de la misma tabla)
a la vez se denominan consultas de unión o join. Estas combinan
filas de una tabla con filas de una segunda tabla, con una expresión que especifica qué
filas se deben emparejar. Por ejemplo, para devolver todos los registros meteorológicos junto
con la ubicación de la ciudad asociada, la base de datos debe comparar la columna
city de cada fila de la tabla weather con la
columna name de todas las filas de la tabla cities,
y seleccionar los pares de filas donde estos valores coincidan.[4]
Esto se lograría con la siguiente consulta:
SELECT * FROM weather JOIN cities ON city = name;
city | temp_lo | temp_hi | prcp | date | name | location
---------------+---------+---------+------+------------+---------------+-----------
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53)
(2 rows)
Observa dos cosas sobre el conjunto de resultados:
No hay ninguna fila de resultado para la ciudad de Hayward. Esto se debe
a que no hay ninguna entrada coincidente en la tabla cities
para Hayward, por lo que la unión ignora las filas no coincidentes en la tabla
weather. Veremos en breve cómo se puede solucionar esto.
Hay dos columnas que contienen el nombre de la ciudad. Esto es correcto
porque las listas de columnas de las tablas weather y
cities se concatenan. Sin embargo, en la práctica esto es
indeseable, por lo que probablemente querrás listar las columnas de salida explícitamente
en lugar de utilizar *:
SELECT city, temp_lo, temp_hi, prcp, date, location
FROM weather JOIN cities ON city = name;
Dado que todas las columnas tenían nombres diferentes, el analizador (parser) encontró automáticamente a qué tabla pertenecen. Si hubiera nombres de columna duplicados en las dos tablas, tendrías que calificar (qualify) los nombres de las columnas para mostrar a cuál te refieres, como en:
SELECT weather.city, weather.temp_lo, weather.temp_hi,
weather.prcp, weather.date, cities.location
FROM weather JOIN cities ON weather.city = cities.name;
Se considera ampliamente un buen estilo calificar todos los nombres de columna en una consulta de unión, de modo que la consulta no falle si más adelante se añade un nombre de columna duplicado a una de las tablas.
Las consultas de unión del tipo visto hasta ahora también se pueden escribir de esta forma:
SELECT *
FROM weather, cities
WHERE city = name;
Esta sintaxis es anterior a la sintaxis JOIN/ON,
que se introdujo en SQL-92. Las tablas simplemente se listan en la cláusula
FROM, y la expresión de comparación se añade a la cláusula
WHERE. Los resultados de esta sintaxis implícita más antigua y de
la más nueva sintaxis explícita JOIN/ON son idénticos.
Pero para un lector de la consulta, la sintaxis explícita hace que su significado sea más
fácil de entender: la condición de unión se introduce mediante su propia palabra clave,
mientras que antes la condición se mezclaba en la cláusula WHERE junto
con otras condiciones.
Now we will figure out how we can get the Hayward records back in.
What we want the query to do is to scan the
weather table and for each row to find the
matching cities row(s). If no matching row is
found we want some “empty values” to be substituted
for the cities table's columns. This kind
of query is called an outer join. (The
joins we have seen so far are inner joins.)
El comando se ve así:
SELECT *
FROM weather LEFT OUTER JOIN cities ON weather.city = cities.name;
city | temp_lo | temp_hi | prcp | date | name | location
---------------+---------+---------+------+------------+---------------+-----------
Hayward | 37 | 54 | | 1994-11-29 | |
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
San Francisco | 43 | 57 | 0 | 1994-11-29 | San Francisco | (-194,53)
(3 rows)
Esta consulta se denomina unión externa izquierda o left outer join porque la tabla mencionada a la izquierda del operador de unión tendrá cada una de sus filas en la salida al menos una vez, mientras que la tabla de la derecha solo tendrá aquellas filas de salida que coincidan con alguna fila de la tabla izquierda. Al emitir una fila de la tabla izquierda para la cual no hay coincidencia en la tabla derecha, se sustituyen por valores vacíos (null) las columnas de la tabla derecha.
Ejercicio: También existen las uniones externas derechas (right outer joins) y las uniones externas completas (full outer joins). Intenta averiguar qué hacen esas.
También podemos unir una tabla consigo misma. Esto se denomina autounión o
self join. Como ejemplo, supongamos que deseamos encontrar todos los
registros meteorológicos que se encuentran en el rango de temperatura de otros registros
meteorológicos. Por lo tanto, necesitamos comparar las columnas temp_lo
y temp_hi de cada fila de la tabla weather con
las columnas temp_lo y temp_hi de todas las
demás filas de la tabla weather. Podemos hacer esto con la siguiente consulta:
SELECT w1.city, w1.temp_lo AS low, w1.temp_hi AS high,
w2.city, w2.temp_lo AS low, w2.temp_hi AS high
FROM weather w1 JOIN weather w2
ON w1.temp_lo < w2.temp_lo AND w1.temp_hi > w2.temp_hi;
city | low | high | city | low | high
---------------+-----+------+---------------+-----+------
San Francisco | 43 | 57 | San Francisco | 46 | 50
Hayward | 37 | 54 | San Francisco | 46 | 50
(2 rows)
Aquí hemos renombrado la tabla weather como w1 y w2
para poder distinguir el lado izquierdo y el derecho de la unión. También puedes utilizar este
tipo de alias en otras consultas para ahorrar algo de escritura, por ejemplo:
SELECT *
FROM weather w JOIN cities c ON w.city = c.name;
Te encontrarás con este estilo de abreviatura con bastante frecuencia.
[4] Este es solo un modelo conceptual. La unión se suele realizar de una manera más eficiente que comparando realmente cada par posible de filas, pero esto es invisible para el usuario.