При решении задач анализа продаж, планирования закупок и расчета среднедневного расхода часто требуется определить, сколько дней за выбранный интервал товар фактически присутствовал на складе, а сколько дней отсутствовал (out-of-stock). Рассмотрим различные подходы к реализации такого расчета в запросе платформы 1С:Предприятие.
Разберем базовое ограничение платформы 1С: виртуальная таблица ОстаткиИОбороты с периодичностью ДЕНЬ в обычном тексте запроса возвращает строки только на те даты, в которые по регистру происходили движения, а также на граничные даты периода. Если в течение недели по позиции не было ни приходов, ни расходов, виртуальная таблица не вернет промежуточные даты. Поэтому просто посчитать количество записей с остатком через функцию КОЛИЧЕСТВО() напрямую из таблицы остатков нельзя — дни без движений выпадут из выборки.
Для решения задачи применяются три основных подхода:
Рассмотрим классический способ на чистом языке запросов. Сформируем таблицу всех календарных дней периода и соединим ее с таблицей среза остатков на каждую дату либо с интервалами движений.
Разберем по шагам построение такого запроса:
Посмотрим на пример формирования и расчета через временную таблицу дат:
ВЫБРАТЬ
Календарь.Дата КАК Период,
СписокНоменклатуры.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ТоварыДни
ИЗ
&ТаблицаКалендаря КАК Календарь
ДРОБНОЕ ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ &ТаблицаНоменклатуры КАК СписокНоменклатуры
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Номенклатура КАК Номенклатура,
ВТ.Период КАК Период,
ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК Остаток
ПОМЕСТИТЬ ВТ_ОстаткиПоДням
ИЗ
ВТ_ТоварыДни КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
,
Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ &ТаблицаНоменклатуры)
) КАК ТоварыНаСкладахОстатки
ПО ВТ.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ОстаткиПоДням.Номенклатура КАК Номенклатура,
СУММА(ВЫБОР
КОГДА ВТ_ОстаткиПоДням.Остаток > 0 ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоДнейСНаличиемОстаткаНаСкладе,
СУММА(ВЫБОР
КОГДА ВТ_ОстаткиПоДням.Остаток <= 0 ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоДнейБезОстаткаНаСкладе
ИЗ
ВТ_ОстаткиПоДням КАК ВТ_ОстаткиПоДням
СГРУППИРОВАТЬ ПО
ВТ_ОстаткиПоДням.Номенклатура
Важное замечание по производительности: Декартово соединение большого массива товаров с длинным периодом (например, 10 000 товаров × 365 дней = 3,65 млн строк) создает высокую нагрузку на СУБД и tempdb. Данный метод подходит для небольших списков позиций или коротких интервалов.
Если расчет выполняется в рамках отчета, оптимальнее всего использовать встроенный функционал Системы компоновки данных. СКД умеет автоматически дополнять отсутствующие периоды в группировках без создания тяжелых временных таблиц дат в SQL-запросе.
Разберем по шагам настройку схемы компоновки данных:
ОстаткиИОбороты регистра накопления с периодичностью ДЕНЬ.ПериодДень указываем роль «Период» и устанавливаем тип дополнения «День», указав параметры начала и окончания периода.
Сумма(Выбор Когда ВНаличииКонечныйОстаток > 0 Тогда 1 Иначе 0 Конец)
Проанализируем наиболее производительный вариант для чистого SQL-запроса на больших объемах данных. Вместо создания строки на каждый день вычисляются интервалы между смежными датами смены остатка.
РАЗНОСТЬДАТ(ДатаНачала, ДатаОкончания, ДЕНЬ) рассчитываем длину интервала, на протяжении которого остаток сохранял положительное или нулевое значение.При расчете дней наличия товара важно учитывать логику предметной области и типовых конфигураций (например, 1С:Управление торговлей или 1С:ERP):
ТоварыНаСкладах) не всегда означает возможность его продажи или закупки. Если весь остаток зарезервирован под заказы клиентов, свободный остаток равен нулю. Для корректного расчета закупки следует использовать регистры доступных остатков (например, ЗапасыИПотребности) по формуле ВНаличии - ВРезерве.0. Чтобы не потерять факт наличия товара в этот день, в условии следует проверять не только конечный остаток, но и приход/расход:
ВЫБОР
КОГДА НачальныйОстаток + Приход > 0 ТОГДА 1
ИНАЧЕ 0
КОНЕЦ
Количество > 0, а не Количество <> 0.