Срок годности товара в SQL: какие партии нужно продать первыми

На складе лежат две партии одного товара. Если отбирать сначала недавно поступившую, старая успеет испортиться. Правило FEFO отдаёт приоритет ближайшему сроку годности. В SQLite построим очередь партий и распределим заказанное количество так, чтобы сотрудник видел, сколько взять из каждой.

Срок годности и свободное количество

Для примера expires — последний календарный день, когда партия допустима к продаже. На дату 5 октября партия со сроком 5 октября ещё разрешена, со сроком 4 октября уже нет. В конкретной компании может действовать минимальный остаточный срок для доставки; тогда границу нужно сдвинуть на необходимое число дней.

В qty храним свободное количество после резервов и брака. Отрицательные и пустые остатки в очередь не входят. Для одного товара партии сортируются по expires, затем по batch_id. Второй ключ делает порядок определённым при одинаковой дате. При нескольких товарах расчёт нужно разделить PARTITION BY sku.

Это рекомендация отбора на снимке, а не блокировка склада. Между расчётом и фактической комплектацией свободное количество способно измениться. Операционная система должна отдельно резервировать выбранные единицы и проверять актуальный остаток; SQL-отчёт показывает понятную логику выбора партий.

Распределяем потребность по очереди

Оконная сумма считает количество в предшествующих партиях. Для первой строки такого количества нет, поэтому COALESCE даёт ноль. Рамка ROWS заканчивается на 1 PRECEDING: текущая партия не входит в уже доступный объём.

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

WITH batches(batch_id,expires,qty) AS (
 VALUES (1,'2026-10-06',5),(2,'2026-10-10',2),
 (3,'2026-10-07',0),(4,'2026-10-01',3)
), eligible AS (
 SELECT * FROM batches WHERE qty>0 AND expires>='2026-10-05'
), queued AS (
 SELECT *,COALESCE(SUM(qty) OVER(ORDER BY expires,batch_id
 ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),0) AS before_qty
 FROM eligible
), allocated AS (
 SELECT batch_id,expires,qty,MAX(0,MIN(qty,6-before_qty)) AS take_qty
 FROM queued
)
SELECT batch_id,expires,qty,take_qty,
 CAST(julianday(expires)-julianday('2026-10-05') AS INTEGER) AS days_left
FROM allocated WHERE take_qty>0 ORDER BY expires,batch_id;

Ожидаемые строки результата в порядке столбцов SELECT:

[[1, "2026-10-06", 5, 5, 1], [2, "2026-10-10", 2, 1, 5]]

Проверяем маршрут отбора

Сотрудник должен взять пять единиц из партии 1 и одну из партии 2. Нулевая партия 3 и просроченная партия 4 исключены. Сумма take_qty равна шести и не превышает свободное количество ни одной партии. Если допустимого запаса меньше заказа, сумма отбора окажется меньше потребности — это отдельный сигнал дефицита.

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

Как оценить риск списания

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

Учитывайте требования клиента и доставки. Партия с одним оставшимся днём может подходить для немедленной розницы и не подходить для заказа с двухдневной перевозкой. В таком случае фильтр допустимости рассчитывается на дату получения клиентом. Именно эта граница должна быть согласована до оконного распределения количества.

При одинаковых датах полезно понимать, как рамка меняет накопительную сумму: ROWS и RANGE в SQL: почему накопительный итог прыгает на одинаковой дате.

Следующий уровень SQL-практики

В «Продвинутом SQL» вы разбираете оконные рамки и последовательные расчёты, а затем решаете цельные задачи с автоматической проверкой. Практика идёт на SQLite в браузере и включает данные с равными датами и пропусками. Итоговый проект объединяет SQL-приёмы в витрину магазина. Откройте программу «Продвинутого SQL».

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