Найти тему
Властелин машин

Операции с множествами в SQL

Оглавление

Без данных операций не обходится ни один сложный запрос в 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
-2

а теперь с 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
-3

Как видим по фрагментам результатов, во втором случае трижды выведена строка с участием 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
-4

Разность результатов запросов

Для ее получения предназначен оператор EXCEPT. Используя представление f_y, выведем фрагмент списка бойцов 1994 г., которые не принимали участие в соревнованиях годом ранее:

SELECT fighter
FROM f_y
WHERE year=1994
EXCEPT
SELECT fighter
FROM f_y
WHERE year=1993
-5

Немного о сортировке

Как можно было заметить выше, при формировании объединительного представления 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)
-6

Соответственно, при ссылке на названия столбцов необходимо использовать первые, иначе получим ошибку. Рассмотрим поведение 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
-7

и по вторым:

(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
-8

-9