Коррелирующие подзапросы

Коррелирующие подзапросы позволяют иногда очень кратко написать запросы, которые могут выглядеть весьма громоздко при использовании других языковых средств. Напомним, что коррелирующий подзапрос — это подзапрос, который содержит ссылку на столбцы из включающего его запроса (назовем его основным). Таким образом, коррелирующий подзапрос будет выполняться для каждой строки основного запроса, так как значения столбцов основного запроса будут меняться.

Пример 8.9.1

Требуется определить дату и рейсы каждого пассажира, совершенные им в свой последний полетный день.

Иными словами, нужно определить максимальную дату полета для каждого пассажира и найти все его рейсы за эту дату. С определением максимальной даты нет никаких проблем:

SELECT id_psg, MAX(date)
FROM pass_in_trip
GROUP BY id_psg;
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]

Однако тут нет рейса. Если мы попытаемся включить рейс в список вывода:

SELECT id_psg, trip_no, MAX(date)
FROM pass_in_trip
GROUP BY id_psg;
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]
то получим сообщение об ошибке, так как номер рейса не используется в агрегатной функции и не входит список столбцов группировки. Если включить номер рейса в этот список:

SELECT id_psg, trip_no, MAX(date)
FROM pass_in_trip
GROUP BY id_psg, trip_no;
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]
мы получим последний полет пассажира каждым рейсом, которым он летал. Это совсем не та задача, которую мы пытаемся решить. Применение коррелирующего подзапроса

SELECT id_psg, trip_no, [date]
FROM pass_in_trip pt_1
WHERE [date] = (SELECT MAX([date])
FROM pass_in_trip pt_2
WHERE pt_1.id_psg = pt_2.id_psg
);
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]
дает то, что нужно:

id_psgtrip_nodate
1011872003-04-14 00:00:00.000
911822003-04-13 00:00:00.000
811872003-04-14 00:00:00.000
611232003-04-08 00:00:00.000
511452003-04-25 00:00:00.000
311452003-04-05 00:00:00.000
311232003-04-05 00:00:00.000
211242003-04-02 00:00:00.000
111002003-04-29 00:00:00.000

Здесь для каждого рейса проверяется, совершен ли он в последний полетный день данного пассажира. При этом если таких рейсов было несколько, мы получим их все.

Очевидным недостатком приведенного решения как раз и является то, что подзапрос должен вычисляться для каждой строки основного запроса. Чтобы избежать этого, можно предложить альтернативное решение, использующее соединение таблицы Pass_in_trip с приведенным в самом начале подзапросом, который вычисляет максимальные даты по каждому пассажиру:

SELECT pt_1.id_psg, trip_no, [date]
FROM pass_in_trip pt_1 JOIN
(SELECT id_psg, MAX([date]) md
FROM pass_in_trip
GROUP BY id_psg
) pt_2 ON pt_1.id_psg = pt_2.id_psg AND
[date] = md;
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]

Важно

Напомним, что приведенные здесь примеры можно выполнить непосредственно на сайте, установив флажок «Без проверки» на странице с упражнениями на SELECT.

Рекомендуемые упражнения: 58