Найти тему

Дополнение к выборке лучших

В личной переписке мне совершенно справедливо заметили, что в постинге про выборку лучших учеников (Выбор максимального значения по группам в SQL) я, любуясь красотой решения через PARTITION, обошёл вниманием ещё одно решение, которое сочетает в себе универсальность первого и скорость второго.

Проверил, оно летает так же быстро, как второе. И при этом оно пойдёт на любой нормальной реляционной СУБД без модификаций, и даже может прийти в голову джуну.

По-русски формулируется почти так же, как второе. Надо найти лучшую успеваемость в каждом классе, а потом в каждом классе взять учеников с лучшей успеваемостью.

А на SQL это формулируется как соединение с вложенным запросом.

SELECT stud.Name

FROM Students stud

INNER JOIN

(

SELECT ClassId, MAX(Performance) as BestPerformance

FROM Students

GROUP BY ClassId

) best

ON stud.ClassId = best.ClassId

WHERE stud.Performance = best.BestPerformance

Выполняется, как второе, за O(n) (без учёта всяческих оптимизаций). На базе в десятки миллионов записей проскочило за треть секунды.
По сравнению с решением через ранг в партиции получилось всё же медленнее, но, во-первых, даже на базе в десятки миллионов записей разница в доли секунды, а во-вторых, такая разница в принципе может быть вопросом оптимизации базы.
Для сравнения – первый вариант (LEFT JOIN с выборкой только тех, у кого партнёра по соединению не нашлось) работал дольше минуты (потом я просто абортнул запрос).

Вывод – третий вариант решения лучший, т.к. сочетает универсальность и скорость.