Использовали ли вы оконные функции в базе данных?
Что проверяют
Умеете ли вы решать на SQL задачи, которые без окон превращаются в самосоединения и коррелированные подзапросы: топ-N в группе, нарастающий итог, сравнение с предыдущей строкой. Для C#-разработчика следом почти всегда идёт вопрос, как такой запрос живёт рядом с EF Core.
Как отвечать
Суть: оконная функция считает значение по набору строк, связанных с текущей, но не схлопывает их, как GROUP BY. Синтаксис функция() OVER (PARTITION BY ... ORDER BY ... ROWS ...). Что стоит назвать:
- ранжирование:
ROW_NUMBERдаёт уникальный номер,RANKпри равенстве оставляет пропуски (1, 1, 3),DENSE_RANKнет (1, 1, 2); - смещение:
LAGиLEADберут значение из предыдущей или следующей строки, удобно для разницы между заказами или дельты метрики; - агрегаты с окном:
SUM,AVG,COUNTсOVERдля нарастающих итогов и скользящих средних, напримерROWS BETWEEN 6 PRECEDING AND CURRENT ROW; - значения из окна:
FIRST_VALUE,LAST_VALUE, аNTILEдля разбиения на равные группы.
Живые примеры: последний заказ каждого клиента через ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY CreatedAt DESC) и фильтр rn = 1; удаление дублей перед созданием уникального индекса; выручка по дням с нарастающим итогом для отчёта.
В проекте на C# такие запросы обычно пишут на SQL: FromSql или Database.SqlQuery<T> в EF Core, либо Dapper, а linq2db умеет выражать окна прямо в LINQ. Для производительности помогает индекс по колонкам из PARTITION BY и ORDER BY: тогда в плане SQL Server пропадает дорогой Sort.
Нужен ваш реальный случай с цифрами. Если окна в работе не применяли, скажите об этом и разберите пример вслух.
Где спотыкаются
Пытаются фильтровать по окну в WHERE. Окна вычисляются после WHERE, поэтому нужен CTE или подзапрос.
Не знают про рамку по умолчанию. С ORDER BY без явной рамки действует RANGE ... CURRENT ROW: строки с одинаковой датой получают одинаковый нарастающий итог, а LAST_VALUE возвращает текущую строку или последнюю из равных ей, а не последнюю в группе.
Путают RANK и DENSE_RANK и берут ROW_NUMBER там, где равные значения должны делить место.