Как выбрать второй по величине ранг цены для артикула
Что проверяют
Умеете ли вы работать с оконными функциями и понимаете ли разницу между способами ранжирования. Почти всегда следом спрашивают: что вернёт ваш запрос, если у артикула две одинаковые максимальные цены, и что будет, если второй цены нет вовсе.
Как отвечать
Сначала уточните, что значит «второй по величине»: вторая уникальная цена или вторая строка после сортировки. От этого зависит функция:
DENSE_RANK(): одинаковые цены получают общий ранг, следующий идёт без пропуска. Цены 100, 100 и 90 дадут ранги 1, 1 и 2, вторая по величине цена будет 90;RANK(): при равенстве ранг тоже общий, но дальше пропуск: 1, 1 и 3. Ранга 2 в таком наборе просто нет;ROW_NUMBER(): сквозная нумерация без учёта равенства, вторая строка вернёт 100.
Для всех артикулов сразу запрос строится через CTE, потому что оконную функцию нельзя фильтровать в WHERE того же уровня:
WITH r AS (SELECT article_id, price, DENSE_RANK() OVER (PARTITION BY article_id ORDER BY price DESC) AS rnk FROM prices) SELECT DISTINCT article_id, price FROM r WHERE rnk = 2
В BigQuery, Snowflake и DuckDB то же самое пишется короче через QUALIFY. Для одного артикула подойдёт и SELECT DISTINCT price ... ORDER BY price DESC LIMIT 1 OFFSET 1, но на весь справочник такой запрос не масштабируется.
Если нужен список всех артикулов, включая те, где второй цены нет, агрегируйте: в PostgreSQL MAX(price) FILTER (WHERE rnk = 2) с GROUP BY article_id вернёт NULL вместо пропавшей строки.
Где спотыкаются
Берут ROW_NUMBER() и получают дубль максимальной цены вместо второй. Выбор функции нужно проговорить вслух, а не оставить на удачу.
Забывают про NULL. В PostgreSQL при ORDER BY price DESC пустые значения по умолчанию идут первыми и занимают ранг 1, поэтому нужен NULLS LAST или фильтр price IS NOT NULL.
Пытаются написать WHERE DENSE_RANK() OVER (...) = 2 и получают ошибку: окна вычисляются после WHERE, фильтровать по ним можно только уровнем выше.