Очередь заявок в SQL: где закончится бюджет при строгом приоритете

У подразделения осталось сто условных единиц бюджета. В очереди три заявки стоимостью 60, 50 и 20. Самая приоритетная проходит, следующая уже не помещается. Можно ли профинансировать третью? Ответ зависит от правила очереди. При строгом порядке она ждёт вместе со второй; при разрешённом пропуске может пройти. Одинаковый лимит не означает одинаковое решение.

Сначала определить, что означает приоритет

Рассмотрим строгую очередь: компания финансирует непрерывное начало списка и останавливается на первой заявке, которая превышает лимит. Так работают процессы, где дешёвая менее важная заявка не должна обгонять дорогостоящую критичную. Запрос не ищет набор с максимальной пользой или максимальным расходованием денег. Он отвечает на операционный вопрос: до какой строки хватит согласованного бюджета.

У заявки есть уникальный id, приоритет и положительная стоимость. Если два приоритета совпали, порядок задаёт id; в рабочем процессе это может быть время поступления и затем id. Без последнего однозначного ключа одинаковые исходные строки могут распределяться в разном порядке, а пограничная заявка будет то попадать в бюджет, то выпадать.

Накопить стоимость в том же порядке

Оконная SUM сохраняет отдельную строку каждой заявки и добавляет сумму всех предшествующих затрат вместе с текущей. Явная рамка ROWS важна: она продвигается по строкам установленной очереди. Проверка running ≤ 100 определяет профинансированную часть. В таблице результата остаются и ожидающие строки, поэтому отчёт объясняет весь список.

Пример выполняется в SQLite; функции и синтаксис ниже относятся к этому диалекту.

WITH requests(id,priority,cost) AS (
 VALUES (1,1,60),(2,2,50),(3,3,20)),
ranked AS (
 SELECT *,SUM(cost) OVER(ORDER BY priority,id ROWS UNBOUNDED PRECEDING) AS running
 FROM requests
)
SELECT id,cost,running,CASE WHEN running<=100 THEN 'fund' ELSE 'wait' END AS decision
FROM ranked ORDER BY priority,id;

Первая строка имеет накопительную стоимость 60 и решение fund. Вторая — 110 и wait. Третья — 130 и wait. Двадцать единиц последней заявки сами по себе поместились бы в остаток сорок, но строгая очередь не разрешает перескочить через вторую. Этот пример стоит включить в согласование требований: он быстро обнаруживает разные ожидания финансов и заказчика.

Почему фильтр до окна меняет задачу

Если сначала убрать все заявки дороже остатка, состав очереди изменится до расчёта. Особенно опасно фильтровать по первоначальному лимиту, а потом считать сумму только выбранных строк: такой приём не реализует последовательное списание. Окно должно видеть весь согласованный список, а фильтрация профинансированных строк выполняется снаружи, после определения running.

Отрицательные стоимости нарушают простое свойство остановки. После превышения бюджета возврат или кредитовая строка может снова опустить накопительный итог ниже лимита. Поэтому возвраты средств сначала включают в величину доступного бюджета по утверждённому правилу, а заявки на расход проверяют на неотрицательность. Для бесплатных заявок отдельно решают, должны ли они ждать за платной блокирующей строкой.

Если нужен другой способ отбора

Разрешённый пропуск требует последовательного решения: для каждой строки нужно знать остаток после ранее принятых заявок. Обычная накопительная сумма всех затрат здесь не подходит, потому что отклонённые расходы не должны уменьшать остаток. Возможны рекурсивный обход или отдельная процедура, но сначала надо утвердить правило: можно ли пропускать, сколько раз возвращаться к ожидающим и когда очередь пересматривается.

Максимизация пользы — ещё одна постановка. У заявок появляется оценка эффекта, а набор ищут с ограничением бюджета. Даже сортировка по отношению пользы к цене для неделимых заявок не гарантирует оптимума. Не называйте накопительный отбор оптимизацией бюджета: отчёт должен прямо описывать, какое организационное правило он исполняет.

Для нескольких бюджетов добавьте идентификатор пула и PARTITION BY в окно. Проверьте, что каждая заявка относится ровно к одному пулу, валюта одна, а сумма уже включает обязательные налоги и доставку. Сохраните снимок очереди и лимит на момент решения. Тогда последующее изменение приоритета не перепишет историю согласования, а финансовый контролёр сможет воспроизвести границу отбора.

Следующий шаг

Следующая полезная задача — разнести очередь по бюджетным пулам, сохранить исторические правила и сверить отбор после обновления данных. Платный курс «Продвинутый SQL» даёт практику оконных рамок, многошаговых CTE и проверки результатов на скрытых тестах. Эти навыки помогают сделать расчёт устойчивым к одинаковым приоритетам и пограничным суммам. Откройте программу курса.

Продолжить на практике