Hasta ahora, nuestras consultas sólo han accedido a una tabla a la vez. Las consultas pueden acceder a varias tablas a la vez, o acceder a la misma tabla de manera que se procesen varias filas de la tabla al mismo tiempo. Una consulta que accede a varias filas de la misma tabla o de diferentes tablas a la vez se denomina consulta de unión. Por ejemplo, digamos que se desea listar todos los registros meteorológicos junto con la ubicación de la ciudad asociada. Para ello, tenemos que comparar la columna de la ciudad de cada fila de la tabla del tiempo con la columna del nombre de todas las filas de la tabla de ciudades, y seleccionar los pares de filas en los que estos valores coincidan.
Nota: Esto es sólo un modelo conceptual. La unión se suele realizar de forma más eficiente que comparando realmente cada posible par de filas, pero esto es invisible para el usuario.
Esto se lograría con la siguiente consulta:
SELECT * FROM weather, cities WHERE 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)
Observe dos cosas sobre el conjunto de resultados:
-
No hay ninguna fila de resultados 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 del tiempo. En breve veremos cómo se puede arreglar esto. -
Hay dos columnas que contienen el nombre de la ciudad. Esto es correcto porque las listas de columnas de la tabla
weather
y de la tablacities
están concatenadas. Sin embargo, en la práctica esto no es deseable, por lo que probablemente querrá enumerar las columnas de salida explícitamente en lugar de utilizar *:SELECT city, temp_lo, temp_hi, prcp, date, location FROM weather, cities WHERE city = name;
Como las columnas tenían todas nombres diferentes, el analizador sintáctico averiguó automáticamente a qué tabla pertenecían, pero es de buen estilo calificar completamente los nombres de las columnas en las consultas join:
SELECT weather.city, weather.temp_lo, weather.temp_hi, weather.prcp, weather.date, cities.location FROM weather, cities WHERE cities.name = weather.city;
Las consultas join del tipo visto hasta ahora también pueden escribirse de esta forma alternativa:
SELECT * FROM weather INNER JOIN cities ON (weather.city = cities.name);
Esta sintaxis no es tan comúnmente utilizada como la anterior, pero la mostramos aquí para ayudar a entender los siguientes temas.
Ahora vamos a averiguar cómo podemos recuperar los registros de Hayward. Lo que queremos que haga la consulta es escanear la tabla weather
y que para cada fila encuentre la fila coincidente cities
. Si no se encuentra ninguna fila coincidente queremos que algunos «valores vacíos» sean sustituidos por las columnas de la tabla cities
. Este tipo de consulta se llama outer join. (Las uniones que hemos visto hasta ahora son uniones internas.) El comando tiene el siguiente aspecto:
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 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 sólo tendrá como salida aquellas filas que coincidan con alguna fila de la tabla izquierda. Cuando se emite una fila de la tabla izquierda para la que no hay ninguna coincidencia con la tabla derecha, se sustituyen los valores vacíos (nulos) para las columnas de la tabla derecha.
Ejercicio: También hay uniones externas derechas y uniones externas completas. Intenta averiguar qué hacen.
También podemos unir una tabla contra sí misma. A esto se le llama self join. Como ejemplo, supongamos que deseamos encontrar todos los registros meteorológicos que están en el rango de temperatura de otros registros meteorológicos. Para ello necesitamos comparar las columnas temp_lo y temp_hi de cada fila weather
con las columnas temp_lo y temp_hi de todas las demás filas weather
. Podemos hacerlo 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, weather W2 WHERE 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 reetiquetado la tabla del tiempo como W1 y W2 para poder distinguir el lado izquierdo y derecho del join. También puede utilizar este tipo de alias en otras consultas para ahorrar algo de escritura, por ejemplo:
SELECT * FROM weather w, cities c WHERE w.city = c.name;
Se encontrará con este estilo de abreviación con bastante frecuencia.