Работали ли вы с оконными функциями в SQL?

«Работали ли вы с оконными функциями в SQL?» — вопрос из категории Базы данных, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, активно использовал оконные функции (Window Functions) в SQL для сложных аналитических запросов без необходимости группировки и свёртки всего набора данных. Они позволяют выполнять вычисления над набором строк, связанных с текущей строкой.

Ключевые сценарии применения:

  • Ранжирование и нумерация: ROW_NUMBER(), RANK(), DENSE_RANK() для построения рейтингов или выборки топ-N записей в каждой группе.
  • Аналитические агрегаты: SUM(...) OVER(...), AVG(...) OVER(...) для расчёта накопительных итогов или скользящих средних.
  • Доступ к данным соседних строк: LAG() и LEAD() для сравнения текущего значения с предыдущим или следующим.

Пример: Ранжирование заказов по сумме внутри каждого клиента

SELECT
    CustomerId,
    OrderId,
    Amount,
    ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY Amount DESC) AS OrderRank
FROM Orders;

Практический пример из C#/EF Core:

var topOrders = await _context.Orders
    .Select(o => new
    {
        o.Id,
        o.CustomerId,
        o.Amount,
        Rank = EF.Functions.Rank() // Использование функции из EF Core 5+
            .Over()
            .PartitionBy(o.CustomerId)
            .OrderByDescending(o => o.Amount)
    })
    .Where(x => x.Rank <= 3) // Выбор топ-3 заказа для каждого клиента
    .ToListAsync();

Важные нюансы:

  1. Производительность: Правильное определение окна (PARTITION BY и ORDER BY) критично для скорости выполнения. Необходимы соответствующие индексы.
  2. Поддержка СУБД: Синтаксис и набор функций могут незначительно отличаться между SQL Server, PostgreSQL, MySQL 8+.
  3. Альтернативы: Для простых случаев в коде можно использовать группировку LINQ, но это менее эффективно для больших данных.