Как выбрать топ-N записей в каждой группе, например три самых дорогих заказа каждого клиента?
- Как взять последнюю запись для каждого пользователя?
Пронумеровать строки внутри группы оконной функцией и отфильтровать номер во внешнем запросе: ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY amount DESC) и WHERE rn <= 3. Если важны «ничьи» — DENSE_RANK или RANK вместо ROW_NUMBER.
- ROW_NUMBER при равных суммах выберет произвольный из них — добавляют второй ключ сортировки
- В PostgreSQL для «одной последней» записи удобно DISTINCT ON
- Альтернатива без окон — LATERAL JOIN с LIMIT (эффективно при индексе)
А ещё Коррелированный подзапрос «сколько дороже меня» работает, но медленнее
Подробно
Почему не GROUP BY. MAX(amount) по клиенту даёт значение, но не всю строку заказа. Оконная функция сохраняет строки и позволяет выбрать нужные.
Ничьи. Если у клиента три заказа по 1000 рублей, ROW_NUMBER возьмёт два из них произвольно; DENSE_RANK ≤ 3 вернёт все заказы с тремя наибольшими различными суммами. Выбор зависит от формулировки задачи — уточнить у интервьюера.
Производительность. Окно сортирует всю таблицу по группе. Если групп много, а нужен топ-1 и есть индекс (customer_id, amount DESC), LATERAL-подзапрос с LIMIT 1 на каждого клиента может быть быстрее.
Пример
WITH ranked AS (
SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY customer_id
ORDER BY amount DESC, order_id) AS rn
FROM orders o
)
SELECT * FROM ranked WHERE rn <= 3;
-- PostgreSQL: последний заказ каждого клиента
SELECT DISTINCT ON (customer_id) *
FROM orders
ORDER BY customer_id, created_at DESC;Что хотят услышать
- Решение через ROW_NUMBER и фильтр снаружи.
- Обработка равных значений.
- Альтернативы: DISTINCT ON, LATERAL.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Оконную функцию с условием rn <= 3 можно фильтровать прямо в WHERE того же запроса
- ✗ORDER BY amount DESC LIMIT 3 по всей таблице
- ✗GROUP BY customer_id и MAX(amount)
- ✓ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY amount DESC), снаружи фильтр rn <= 3
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.