В этом уроке мы разберем оператор UNION
. Чтобы попрактиковаться, мы возьмем нашу базу данных. В ней есть две таблицы с информацией о студентах, которые записывались на курсы в 2022 и 2023 году:
- course_members_2022
- course_members_2023
Обе таблицы имеют одинаковую структуру:
- Имя пользователя
- Название курса
- Дата записи на курс
Попробуем выяснить, кто записывался на курс по PHP в каждом году. Для этого выполним два запроса:
SELECT username FROM course_members_2022
WHERE coursename = 'PHP';
SELECT username FROM course_members_2023
WHERE coursename = 'PHP'
Выполнив эти запросы, мы получим две таблицы:
- С четырьмя пользователями за 2022 год
- С одним пользователем за 2023 год
php_course_memebers_22
username |
---|
Kendall50 |
Abelardo58 |
Elwyn17 |
Justin87 |
php_course_memebers_23
username |
---|
Leola.Ward |
View on DB Fiddle
Оператор UNION
Теперь объединим результаты этих запросов. Воспользуемся оператором UNION
, который объединяет два запроса в один:
SELECT username FROM course_members_2022
WHERE coursename = 'PHP'
UNION
SELECT username FROM course_members_2023
WHERE coursename = 'PHP';
В результате мы получим пять строк:
php_course_memebers
username |
---|
Elwyn17 |
Kendall50 |
Justin87 |
Abelardo58 |
Leola.Ward |
View on DB Fiddle
Теперь попробуем объединить несколько запросов. Для этого укажем оператор UNION
после каждого запроса:
SELECT username FROM course_members_2022
WHERE coursename = 'PHP'
UNION
SELECT username FROM course_members_2022
WHERE coursename = 'SQL'
UNION
SELECT username FROM course_members_2022
WHERE coursename = 'Javascript';
course_memebers_2022
username |
---|
Elwyn17 |
Kendall50 |
Curtis_Baumbach |
Michaela11 |
Justin87 |
Abelardo58 |
Duncan3 |
View on DB Fiddle
Обратите внимание, что объединять можно далеко не все запросы. Следите, чтобы выполнялись два правила:
- Во всех таблицах должно быть одинаковое количество столбцов
- Во всех таблицах должны быть одинаковые типы данных
Одинаковое количество столбцов
Если мы хотим объединить два подзапроса, они должны работать с одинаковым количеством столбцов. Посмотрите на запрос ниже:
SELECT username, created_at FROM course_members_2022
WHERE coursename = 'PHP'
UNION
SELECT username FROM course_members_2023
WHERE coursename = 'PHP';
Этот запрос выполнить не получится, потому что есть разница в столбцах:
- В первом подзапросе мы выводим два столбца — имя пользователя и дату записи
- Во втором подзапросе мы выводим только один столбец — имя пользователя
Чтобы исправить эту проблему, подправим второй подзапрос. Нам нужно либо вывести поле created_at
в качестве второго столбца, либо заполнить этот столбец значением NULL
.
Этот запрос выведет поле created_at
для обоих подзапросов:
SELECT
username,
created_at
FROM course_members_2022
WHERE coursename = 'PHP'
UNION
SELECT
username,
created_at
FROM course_members_2023
WHERE coursename = 'PHP';
php_course_memebers
username | created_at |
---|---|
Leola.Ward | 2023-01-06T01:52:34.914Z |
Justin87 | 2022-06-22T01:45:38.476Z |
Elwyn17 | 2022-06-21T10:52:36.105Z |
Abelardo58 | 2022-06-18T04:52:36.764Z |
Kendall50 | 2022-06-15T11:33:56.286Z |
View on DB Fiddle
А этот — выведет во втором столбце значение NULL
:
SELECT
username,
created_at
FROM course_members_2022
WHERE coursename = 'PHP'
UNION
SELECT
username,
NULL
FROM course_members_2023
WHERE coursename = 'PHP';
php_course_memebers
username | created_at |
---|---|
Leola.Ward | null |
Justin87 | 2022-06-22T01:45:38.476Z |
Elwyn17 | 2022-06-21T10:52:36.105Z |
Abelardo58 | 2022-06-18T04:52:36.764Z |
Kendall50 | 2022-06-15T11:33:56.286Z |
View on DB Fiddle
Одинаковые типы данных
Чтобы могли объединять запросы, важно и второе правило — чтобы оба запроса работали с одинаковыми типами данных. Попробуем объединить два подзапроса:
- В первом запросе укажем столбы
username
иcreated_at
(текст и дата) - Во втором запросе укажем столбцы
created_at
иusername
(дата и текст, то есть в обратном порядке)
Столбцы в подзапросах работают с разными типами данных, поэтому такой запрос не выполнится:
SELECT
username,
created_at
FROM course_members_2022
WHERE coursename = 'PHP'
UNION
SELECT
created_at,
username
FROM course_members_2023
WHERE coursename = 'PHP';
С этим правилом нужно быть аккуратнее. Дело в том, что СУБД проверяет только совпадение типов данных, но не их смысл. Посмотрим, к чему это может привести. В первом подзапросе укажем столбцы coursename
и username
, а во втором подзапросе - наоборот:
SELECT
coursename,
username
FROM course_members_2022
UNION
SELECT
username,
coursename
FROM course_members_2023
Запрос выполнится успешно, но в результирующей таблице мы увидим вперемешку ники студентов и названия курса. Это не то, что мы хотели получить.
course_memebers
coursename | username |
---|---|
Javascript | Michaela11 |
PHP | Kendall50 |
Ruby | Duncan3 |
Kendall50 | SQL |
Kira94 | SQL |
Sylvan_Nader | Python |
... | ... |
View on DB Fiddle
Нет какого-то автоматического способа предотвратить эту ошибку, поэтому вам нужно следить за смыслом запроса самостоятельно.
Оператор UNION ALL
Перейдем к оператору UNION ALL
и создадим два запроса о пользователях в 2022 году. В первом запросе изучим студентов курсов по Javascript, а во втором — курсов по Python:
SELECT username FROM course_members_2022
WHERE coursename = 'Javascript';
SELECT username FROM course_members_2022
WHERE coursename = 'Python';
Первый запрос выведет трех пользователей, а второй — двух. В целом мы получили пять записей.
js_course_memebers
username |
---|
Duncan3 |
Michaela11 |
Kendall50 |
python_course_memebers
username |
---|
Duncan3 |
Michaela11 |
View on DB Fiddle
А теперь объединим эти запросы с помощью UNION
:
SELECT username FROM course_members_2022
WHERE coursename = 'Javascript'
UNION
SELECT username FROM course_members_2022
WHERE coursename = 'Python';
Этот запрос выведет нам только три записи.
course_memebers
username |
---|
Duncan3 |
Michaela11 |
Kendall50 |
View on DB Fiddle
Почему так произошло? Дело в том, что при объединении подзапросов UNION
по умолчанию убирает дублирующиеся строки. Чтобы этого не происходило, добавим ALL
к оператору UNION
:
SELECT username FROM course_members_2022
WHERE coursename = 'Javascript'
UNION ALL
SELECT username FROM course_members_2022
WHERE coursename = 'Python';
Такой запрос вывел нам пять строк, среди которых есть два одинаковых пользователя — Michaela11 и Duncan3.
course_memebers
username |
---|
Duncan3 |
Michaela11 |
Kendall50 |
Michaela11 |
Duncan3 |
View on DB Fiddle
SELECT username FROM course_members_2022
WHERE coursename = 'Javascript'
UNION ALL
SELECT username FROM course_members_2022
WHERE coursename = 'Python'
ORDER BY username;
course_memebers
username |
---|
Duncan3 |
Duncan3 |
Kendall50 |
Michaela11 |
Michaela11 |
View on DB Fiddle
Обратим внимание, что оператор
ORDER BY
должен идти только в последнем подзапросе, если мы укажем его в первом, то такой запрос не сработает:
SELECT username FROM course_members_2022
WHERE coursename = 'Javascript'
ORDER BY username
UNION ALL
SELECT username FROM course_members_2022
WHERE coursename = 'Python';
Выводы
В этом уроке вы научились объединять запросы с помощью оператора UNION
. Чтобы запросы срабатывали, важно соблюдать два правила: объединяемые запросы должны иметь одинаковое количество столбцов, а сами столбцы — иметь одинаковые типы данных. По умолчанию UNION
удаляет дублирующиеся строки. Если повторы нужно оставить, можно использовать оператор UNION ALL
.
Обратим внимание, что запрос вывел результаты в произвольном порядке, никак не упорядоченными.
Чтобы упорядочить результаты, нам нужно воспользоваться оператором ORDER BY
в последнем подзапросе. Если нужно упорядочить строки, то оператор ORDER BY
нужно указывать в конец последнего подзапроса.
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.