Запрос нескольких таблиц Чаще всего база данных содержит несколько таблиц, каждая из которых содержит разные наборы данных. SQL предоставляет несколько разных способов выполнения одного запроса для нескольких таблиц.
Предложение JOIN может использоваться для объединения строк из двух или более таблиц в результате запроса. Это достигается путем нахождения связанного столбца между таблицами и соответствующей сортировки результатов в выходных данных.
Операторы SELECT, которые включают предложениеJOIN, обычно следуют этому синтаксису:
SELECT table1.column1, table2.column2
FROM table1
JOIN table2 ON table1.related_column=table2.related_column;
Обратите внимание, что поскольку предложения JOIN сравнивают содержимое нескольких таблиц, в предыдущем примере указывается, из какой таблицы выбрать каждый столбец, предшествуя имени столбца с именем таблицы и точкой. Вы можете указать, из какой таблицы должен быть выбран столбец, например, для любого запроса, хотя это не обязательно при выборе из одной таблицы, как мы делали в предыдущих разделах. Давайте рассмотрим пример, используя наш образец Dата.
Представьте, что вы хотите купить каждому из ваших друзей пару ботинок для боулинга в качестве подарка на день рождения. Поскольку информация о датах рождения и размерах обуви ваших друзей хранится в отдельных таблицах, вы можете запросить обе таблицы по отдельности, а затем сравнить результаты для каждой из них. Тем не менее, с помощью предложения JOIN вы можете найти всю необходимую информацию с помощью одного запроса:
SELECT tourneys.name, tourneys.size, dinners.birthdate
FROM tourneys
JOIN dinners ON tourneys.name=dinners.name;
+---------+------+------------+
| name | size | birthdate |
+---------+------+------------+
| Dolly | 8.5 | 1946-01-19 |
| Etta | 9 | 1938-01-25 |
| Irma | 7 | 1941-02-18 |
| Barbara | 7.5 | 1948-12-25 |
| Gladys | 8 | 1944-05-28 |
+---------+------+------------+
5 rows in set (0.00 sec)
Предложение JOIN, используемое в этом примере, без каких-либо других аргументов, является предложением innerJOIN. Это означает, что он выбирает все записи, которые имеют совпадающие значения в обеих таблицах, и печатает их в наборе результатов, в то время как все несоответствующие записи исключаются. Чтобы проиллюстрировать эту идею, давайте добавим новую строку в каждую таблицу, у которой нет соответствующей записи в другой:
INSERT INTO tourneys (name, wins, best, size)
VALUES ('Bettye', '0', '193', '9');
INSERT INTO dinners (name, birthdate, entree, side, dessert)
VALUES ('Lesley', '1946-05-02', 'steak', 'salad', 'ice cream');
Затем повторно запустите предыдущий оператор SELECT с предложением JOIN:
SELECT tourneys.name, tourneys.size, dinners.birthdate
FROM tourneys
JOIN dinners ON tourneys.name=dinners.name;
+---------+------+------------+
| name | size | birthdate |
+---------+------+------------+
| Dolly | 8.5 | 1946-01-19 |
| Etta | 9 | 1938-01-25 |
| Irma | 7 | 1941-02-18 |
| Barbara | 7.5 | 1948-12-25 |
| Gladys | 8 | 1944-05-28 |
+---------+------+------------+
5 rows in set (0.00 sec)
Обратите внимание, что, поскольку в таблице «tourneys» нет записи для Лесли, а в таблице «dinners» нет записи для Бетти, эти записи отсутствуют в этих выходных данных.
Тем не менее, можно вернуть все записи из одной из таблиц, используя предложение outer JOIN. В MySQL предложения JOIN записываются какLEFT JOIN или RIGHT JOIN.
Предложение LEFT JOIN возвращает все записи из« левой »таблицы и только совпадающие записи из правой таблицы. В контексте внешних объединений левая таблица - это таблица, на которую ссылается условие FROM, а правая таблица - любая другая таблица, на которую ссылается после оператораJOIN.
Выполните предыдущий запрос еще раз, но на этот раз используйте предложение LEFT JOIN:
SELECT tourneys.name, tourneys.size, dinners.birthdate
FROM tourneys
LEFT JOIN dinners ON tourneys.name=dinners.name;
Эта команда будет возвращать каждую запись из левой таблицы (в данном случае, «турниры»), даже если в правой таблице нет соответствующей записи. Каждый раз, когда в правой таблице нет подходящей записи, она возвращается как NULL или просто пустое значение, в зависимости от вашей RDBMS:
+---------+------+------------+
| name | size | birthdate |
+---------+------+------------+
| Dolly | 8.5 | 1946-01-19 |
| Etta | 9 | 1938-01-25 |
| Irma | 7 | 1941-02-18 |
| Barbara | 7.5 | 1948-12-25 |
| Gladys | 8 | 1944-05-28 |
| Bettye | 9 | NULL |
+---------+------+------------+
6 rows in set (0.00 sec)
Теперь выполните запрос еще раз, на этот раз с предложением RIGHT JOIN:
SELECT tourneys.name, tourneys.size, dinners.birthdate
FROM tourneys
RIGHT JOIN dinners ON tourneys.name=dinners.name;
Это вернет все записи из правой таблицы (dinners). Поскольку дата рождения Лесли записана в правой таблице, но для нее нет соответствующей строки в левой таблице, столбцы name иsize вернутся как значения NULL в этой строке:
+---------+------+------------+
| name | size | birthdate |
+---------+------+------------+
| Dolly | 8.5 | 1946-01-19 |
| Etta | 9 | 1938-01-25 |
| Irma | 7 | 1941-02-18 |
| Barbara | 7.5 | 1948-12-25 |
| Gladys | 8 | 1944-05-28 |
| NULL | NULL | 1946-05-02 |
+---------+------+------------+
6 rows in set (0.00 sec)
Обратите внимание, что левые и правые объединения могут быть записаны как LEFT OUTER JOIN илиRIGHT OUTER JOIN, хотя подразумевается часть «OUTER» в предложении. Аналогично, указание INNER JOIN даст тот же результат, что и простое написаниеJOIN.
В качестве альтернативы использованию JOIN для запроса записей из нескольких таблиц, вы можете использовать предложениеUNION.
Оператор UNION работает немного иначе, чем предложениеJOIN: вместо того, чтобы печатать результаты из нескольких таблиц в виде уникальных столбцов с использованием одного оператора SELECT,UNION объединяет результаты двух операторов SELECT в один столбец.
Чтобы проиллюстрировать, запустите следующий запрос:
SELECT name FROM tourneys UNION SELECT name FROM dinners;
Этот запрос удалит все повторяющиеся записи, что является поведением по умолчанию оператора UNION:
+---------+
| name |
+---------+
| Dolly |
| Etta |
| Irma |
| Barbara |
| Gladys |
| Bettye |
| Lesley |
+---------+
7 rows in set (0.00 sec)
Чтобы вернуть все записи (включая дубликаты), используйте оператор UNION ALL:
SELECT name FROM tourneys UNION ALL SELECT name FROM dinners;
+---------+
| name |
+---------+
| Dolly |
| Etta |
| Irma |
| Barbara |
| Gladys |
| Bettye |
| Dolly |
| Etta |
| Irma |
| Barbara |
| Gladys |
| Lesley |
+---------+
12 rows in set (0.00 sec)
Имена и количество столбцов в таблице результатов отражают имя и количество столбцов, запрошенных первым оператором SELECT. Обратите внимание, что при использовании UNION для запроса нескольких столбцов из более чем одной таблицы каждый операторSELECT должен запрашивать одинаковое количество столбцов, соответствующие столбцы должны иметь одинаковые типы данных, а столбцы в каждом операторе SELECT должны быть в том же порядке. В следующем примере показано, что может произойти, если вы используете предложение UNION для двух операторовSELECT, которые запрашивают разное количество столбцов:
SELECT name FROM dinners UNION SELECT name, wins FROM tourneys;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
Другой способ запроса нескольких таблиц - использование subqueries. Подзапросы (также известные как inner или nested query) - это запросы, заключенные в другой запрос. Это полезно в тех случаях, когда вы пытаетесь отфильтровать результаты запроса по сравнению с результатами отдельной агрегатной функции.
Чтобы проиллюстрировать эту идею, скажем, вы хотите знать, кто из ваших друзей выиграл больше матчей, чем Барбара. Вместо того, чтобы узнать, сколько матчей выиграла Барбара, а затем выполнить другой запрос, чтобы узнать, кто выиграл больше игр, вы можете рассчитать обе с помощью одного запроса:
SELECT name, wins FROM tourneys
WHERE wins > (
SELECT wins FROM tourneys WHERE name = 'Barbara' );
+--------+------+
| name | wins |
+--------+------+
| Dolly | 7 |
| Etta | 4 |
| Irma | 9 |
| Gladys | 13 |
+--------+------+
4 rows in set (0.00 sec)
Подзапрос в этом операторе был выполнен только один раз; нужно было только найти значение из столбца wins в той же строке, что иBarbara в столбце name, а данные, возвращаемые подзапросом и внешним запросом, не зависят друг от друга. Однако существуют случаи, когда внешний запрос должен сначала прочитать каждую строку в таблице и сравнить эти значения с данными, возвращенными подзапросом, чтобы получить требуемые данные. В этом случае подзапрос называется коррелированным подзапросом.
Следующее утверждение является примером коррелированного подзапроса. Этот запрос пытается выяснить, кто из ваших друзей выиграл больше игр, чем в среднем для тех, у кого одинаковый размер обуви:
SELECT name, size FROM tourneys AS t
WHERE wins > (
SELECT AVG(wins) FROM tourneys WHERE size = t.size
);
Чтобы запрос завершился, он должен сначала собрать столбцы name иsize из внешнего запроса. Затем он сравнивает каждую строку из этого набора результатов с результатами внутреннего запроса, который определяет среднее количество побед для людей с одинаковыми размерами обуви. Поскольку у вас есть только два друга с одинаковым размером обуви, в наборе результатов может быть только одна строка:
+------+------+
| name | size |
+------+------+
| Etta | 9 |
+------+------+
1 row in set (0.00 sec)
Как упоминалось ранее, подзапросы могут использоваться для запроса результатов из нескольких таблиц. Чтобы проиллюстрировать это одним последним примером, скажем, вы хотели устроить неожиданный ужин для лучшего боулера группы. Вы можете узнать, кто из ваших друзей имеет лучший рекорд в боулинге, и вернуть любимое блюдо по следующему запросу:
SELECT name, entree, side, dessert
FROM dinners
WHERE name = (SELECT name FROM tourneys
WHERE wins = (SELECT MAX(wins) FROM tourneys));
+--------+--------+-------+-----------+
| name | entree | side | dessert |
+--------+--------+-------+-----------+
| Gladys | steak | fries | ice cream |
+--------+--------+-------+-----------+
1 row in set (0.00 sec)
Обратите внимание, что этот оператор не только включает подзапрос, но также содержит подзапрос в этом подзапросе.
Заключение Выдача запросов является одной из наиболее часто выполняемых задач в области управления базами данных. Существует ряд инструментов администрирования баз данных, таких как phpMyAdmin или pgAdmin, которые позволяют выполнять запросы и визуализировать результаты, но с помощью операторов SELECT это все еще широко распространенный рабочий процесс, который также может предоставить вам больший контроль.