Множественные операции в SQL

Теория: Объединение запросов

В этом уроке мы разберем оператор UNION. Чтобы попрактиковаться, мы возьмем нашу базу данных. В ней есть две таблицы с информацией о студентах, которые записывались на курсы в 2022 и 2023 году:

  • course_members_2022
  • course_members_2023

Обе таблицы имеют одинаковую структуру:

  • Имя пользователя
  • Название курса
  • Дата записи на курс

Попробуем выяснить, кто записывался на курс по PHP в каждом году. Для этого выполним два запроса:

SELECT username FROM course_members_2022
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

Теперь попробуем объединить несколько запросов. Для этого укажем оператор 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

usernamecreated_at
Leola.Ward2023-01-06T01:52
.914Z
Justin872022-06-22T01:45
.476Z
Elwyn172022-06-21T10:52
.105Z
Abelardo582022-06-18T04:52
.764Z
Kendall502022-06-15T11:33
.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

usernamecreated_at
Leola.Wardnull
Justin872022-06-22T01:45
.476Z
Elwyn172022-06-21T10:52
.105Z
Abelardo582022-06-18T04:52
.764Z
Kendall502022-06-15T11:33
.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

coursenameusername
JavascriptMichaela11
PHPKendall50
RubyDuncan3
Kendall50SQL
Kira94SQL
Sylvan_NaderPython
......

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 нужно указывать в конец последнего подзапроса.

Завершено

0 / 5