Разберу одну SQL-задачу, на которой спотыкаются даже те, кто уверенно пишет оконные функции.
Звучит безобидно: «выведи 3 самых дорогих товара в каждой категории».
Большинство пишет сразу: ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) — и фильтр rn <= 3. Запрос работает, можно сдавать?
Не совсем. Есть вопрос, который отличает уверенное решение: а что делать с ничьими?
Представь: в категории четыре товара по 1000 ₽. ROW_NUMBER пронумерует их 1, 2, 3, 4 — и четвертый в топ не попадет. Какой именно не попадет? Случайный. При следующем запуске — возможно, другой. Для отчета, на который смотрят люди, это тихая бомба.
У оконных функций три способа нумеровать, и ведут они себя по-разному:
— ROW_NUMBER: 1, 2, 3, 4. Ничьих не существует, порядок внутри ничьей не определен.
— RANK: 1, 1, 1, 1, 5. Ничьи получают один ранг, следующий — с пропуском.
— DENSE_RANK: 1, 1, 1, 1, 2. То же, но без пропусков.
Правильного ответа нет — есть правильное уточнение. «Ровно 3 строки» — это ROW_NUMBER, но тогда добавь в ORDER BY еще и id, чтобы результат был воспроизводимым. «Все товары трех самых высоких цен» — DENSE_RANK <= 3. «Топ-3 с честными местами, как в спорте» — RANK.
На собесе вопрос «а что с ничьими?» часто стоит больше, чем идеальный синтаксис. Потому что это вопрос человека, который уже ловил такие баги в проде.
И в реальной работе все то же самое: молчаливый ROW_NUMBER в отчете — это случайно выпавшая строка, которую никто не заметит. Пока она не окажется чьим-то товаром.
Post #50
123
- ❤ 4