Без данных операций не обходится ни один сложный запрос в SQL. В этой статье я разберу каждую из них на примерах, чтобы лучше понять их особенности и отличия.
В демонстрационных целях будем работать с таблицей всех поединков UFC, загруженных в базу данных PostgreSQL под названием ufc_stat (5960 строк и 205 столбцов, фрагмент ниже):
Объединение результатов запросов
Выполняется с помощью операторов UNION ALL или UNION (дополнительно исключает дубли строк). Построим на основании ufc_stat таблицу из двух столбцов - имена бойцов и годы выступлений (если один и тот же выступал в разные годы, данные записи дублями считаться не будут):
(SELECT fighter_left AS fighter, EXTRACT(YEAR FROM date) AS year FROM ufc_stat
UNION
SELECT fighter_right AS fighter, EXTRACT(YEAR FROM date) AS year FROM ufc_stat)
ORDER BY fighter, year
а теперь с UNION ALL:
(SELECT fighter_left AS fighter, EXTRACT(YEAR FROM date) AS year FROM ufc_stat
UNION ALL
SELECT fighter_right AS fighter, EXTRACT(YEAR FROM date) AS year FROM ufc_stat)
ORDER BY fighter, year
Как видим по фрагментам результатов, во втором случае трижды выведена строка с участием Aaron Riley в 2009 г.
Пересечение результатов запросов
Осуществляется с помощью оператора INTERSECT. Продемонстрируем его работу на примере вывода перечня бойцов, которые принимали участие хотя бы в одном турнире UFC и в 1993, и в 1994 гг.
Для начала сохраним вариант указанной выше таблицы без дублей в представление f_y:
CREATE VIEW f_y (fighter, year)
AS
(SELECT fighter_left AS fighter, EXTRACT(YEAR FROM date) AS year FROM ufc_stat
UNION
SELECT fighter_right AS fighter, EXTRACT(YEAR FROM date) AS year FROM ufc_stat)
Теперь извлечем то, что ищем:
SELECT fighter
FROM f_y
WHERE year=1994
INTERSECT
SELECT fighter
FROM f_y
WHERE year=1993
Разность результатов запросов
Для ее получения предназначен оператор EXCEPT. Используя представление f_y, выведем фрагмент списка бойцов 1994 г., которые не принимали участие в соревнованиях годом ранее:
SELECT fighter
FROM f_y
WHERE year=1994
EXCEPT
SELECT fighter
FROM f_y
WHERE year=1993
Немного о сортировке
Как можно было заметить выше, при формировании объединительного представления f_y стообцам в запросах мы дали одинаковые имена. В противном случае SQL большее значение придает наименованиям в первом запросе и, если они будут отличаться от остальных, в результате будут фигурировать именно они:
(SELECT fighter_left AS fighter1, EXTRACT(YEAR FROM date) AS year1 FROM ufc_stat
UNION
SELECT fighter_right AS fighter2, EXTRACT(YEAR FROM date) AS year2 FROM ufc_stat)
Соответственно, при ссылке на названия столбцов необходимо использовать первые, иначе получим ошибку. Рассмотрим поведение SQL при сортировке по различным наименованиям столбцов в запросах:
(SELECT fighter_left AS fighter1, EXTRACT(YEAR FROM date) AS year1 FROM ufc_stat
UNION
SELECT fighter_right AS fighter2, EXTRACT(YEAR FROM date) AS year2 FROM ufc_stat)
ORDER BY fighter1, year1
и по вторым:
(SELECT fighter_left AS fighter1, EXTRACT(YEAR FROM date) AS year1 FROM ufc_stat
UNION
SELECT fighter_right AS fighter2, EXTRACT(YEAR FROM date) AS year2 FROM ufc_stat)
ORDER BY fighter2, year2