Объединения между таблицами
Эта страница переведена при помощи нейросети GigaChat.
До сих пор запросы обращались только к одной таблице за раз. Запросы могут обращаться к нескольким таблицам одновременно или к одной и той же таблице таким образом, что одновременно обрабатываются несколько строк таблицы. Запросы, которые обращаются к нескольким таблицам (или нескольким экземплярам одной таблицы) одновременно, называются объединенными запросами. Они объединяют строки из одной таблицы со строками из второй таблицы, причем в выражении указывается, какие строки должны быть объединены в пару. Например, чтобы вернуть все записи о погоде с указанием местоположения связанного с ней города, базе данных нужно сравнить столбец "город" каждой строки таблицы погоды со столбцом "название" всех строк таблицы городов и выбрать те пары строк, в которых эти значения совпадают. Это можно сделать с помощью следующего запроса:
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)
Примечание:
Это лишь концептуальная модель. Обычно объединение выполняется более эффективным способом, чем реальное сравнение каждой возможной пары строк, но это незаметно для пользователя.
Обратите внимание на два момента в наборе результатов:
- Для города Хейворд нет строки результата. Это происходит потому, что в таблице городов нет совпадающей записи для Хейворда, поэтому объединение игнорирует несопоставленные строки в таблице погоды. В ближайшее время увидим, как это можно исправить.
- Есть два столбца, содержащие название города. Это правильно, потому что списки столбцов из таблиц
weather
иcities
объединяются. Однако на практике это нежелательно, поэтому, вероятно, захочется перечислить выходные столбцы явно, а не использовать*
:
SELECT city, temp_lo, temp_hi, prcp, date, location
FROM weather JOIN cities ON city = name;
Поскольку все столбцы имеют разные имена, парсер автоматически определил, к какой таблице они относятся. Если бы имена столбцов в двух таблицах дублировались, необходимо было бы уточнить имена столбцов, чтобы указать, какой именно имеется в виду, например:
SELECT weather.city, weather.temp_lo, weather.temp_hi,
weather.prcp, weather.date, cities.location
FROM weather JOIN cities ON weather.city = cities.name;
Считается хорошим тоном уточнять все имена столбцов в запросе join
, чтобы запрос не завершился неудачей, если впоследствии в одну из таблиц будет добавлено дублирующееся имя столбца.
В таком виде могут быть написаны и стыковочные запросы того типа, который рассматривали до сих пор:
SELECT *
FROM weather, cities
WHERE city = name;
Этот синтаксис предшествует синтаксису JOIN/ON
, который был введен в SQL-92. Таблицы просто перечисляются в предложении FROM
, а выражение сравнения добавляется в предложении WHERE
. Результаты, полученные с помощью этого старого неявного синтаксиса и более нового явного синтаксиса JOIN/ON
, идентичны. Но для читателя запроса явный синтаксис облегчает понимание его смысла – условие объединения вводится отдельным ключевым словом, в то время как раньше оно вставлялось в предложение WHERE
вместе с другими условиями.
Теперь выясним, как вернуть записи о Хейворде. Требуется чтобы запрос сканировал weather table
и для каждой строки находил соответствующую строку (строки) cities
. Если ни одна строка не найдена, то хотим, чтобы в столбцы cities table
были подставлены «пустые значения». Такой запрос называется внешним соединением. До сих пор рассматривались внутренние соединения. Команда выглядит следующим образом:
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)
Этот запрос называется левым внешним соединением, поскольку таблица, упомянутая слева от оператора соединения, будет иметь каждую из своих строк в выводе хотя бы один раз, в то время как в таблице справа будут выведены только те строки, которые совпадают с какой-либо строкой левой таблицы. При выводе строки левой таблицы, для которой нет соответствия в правой таблице, в столбцы правой таблицы подставляются пустые (нулевые) значения.
Упражнение: Существуют также правые внешние объединения и полные внешние объединения. Попробуйте выяснить, что они делают.
Также можем объединить таблицу с самой собой. Это называется самоприсоединением. В качестве примера предположим, что требуется найти все погодные записи, которые находятся в диапазоне температур других погодных записей. Поэтому нужно сравнить столбцы temp_lo
и temp_hi
каждой weather rows со столбцами temp_lo
и temp_hi
всех других weather rows
. Можно сделать это с помощью следующего запроса:
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)
Здесь переименована таблица погоды в w1
и w2
, чтобы можно было различать левую и правую стороны соединения. Подобные псевдонимы можно использовать и в других запросах, чтобы сэкономить на наборе текста, например:
SELECT *
FROM weather w JOIN cities c ON w.city = c.name;
Этот стиль сокращений встречается довольно часто.