Как получить остатки номенклатуры на разные даты в одном запросе

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С часто возникает задача: имеется список номенклатуры, и для каждой позиции указана своя дата, на которую нужно получить остаток. Стандартная виртуальная таблица Остатки принимает в качестве параметра Период только одно значение (одну дату), что делает невозможным получение данных для целого списка разных дат одним простым обращением. Однако существует несколько эффективных стратегий решения этой задачи. В этой статье мы подробно разберем каждый из способов, проанализируем их плюсы и минусы, а также посмотрим на примеры реализации.

Способ 1. Использование связанных наборов данных в СКД

Этот метод считается наиболее «элегантным» и производительным. Платформа 1С в Системе компоновки данных (СКД) умеет эффективно обрабатывать связи между наборами данных, передавая параметры из одного запроса в другой.

Разберем алгоритм настройки по шагам:

  1. Создадим первый набор данных (запрос): В нем мы выбираем нашу исходную таблицу, содержащую поля Номенклатура и ДатаОстатка.
  2. Создадим второй набор данных (запрос): В этом запросе мы обращаемся к виртуальной таблице Остатки (например, регистра ТоварыНаСкладах). В параметрах виртуальной таблицы укажем &Период, а в условиях — Номенклатура = &Номенклатура.
  3. Настроим связи наборов данных: Перейдем на закладку «Связи наборов данных». Укажем, что Набор1 является источником, а Набор2 — приемником.
    • В поле «Источник связи» выберем Номенклатура и ДатаОстатка.
    • В поле «Приемник связи» выберем соответствующие поля или параметры (Номенклатура и Период).

Проанализируем ситуацию: при выполнении отчета СКД сама сформирует серию запросов к остаткам, передавая туда конкретные значения номенклатуры и дат. Это избавляет нас от необходимости писать сложные вложенные циклы или перегружать сервер БД тяжелыми соединениями.

Способ 2. Расчет остатка через таблицу оборотов (Накопительный итог)

Если нам необходимо решить задачу строго внутри одного запроса без использования СКД, мы можем воспользоваться методом суммирования всех движений с начала времен до целевой даты. Посмотрим на пример реализации такого запроса:


// 1. Помещаем наши исходные данные во временную таблицу
ВЫБРАТЬ
    ИсходныеДанные.Номенклатура КАК Номенклатура,
    ИсходныеДанные.Дата КАК Дата
ПОМЕСТИТЬ втНомДата
ИЗ
    &ТЗДанных КАК ИсходныеДанные;

////////////////////////////////////////////////////////////////////////////////
// 2. Получаем все обороты по нужной номенклатуре
ВЫБРАТЬ
    ТоварыОбороты.Номенклатура КАК Номенклатура,
    ТоварыОбороты.Период КАК Период,
    ТоварыОбороты.КоличествоОборот КАК КоличествоОборот
ПОМЕСТИТЬ втОбороты
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты(,, День, Номенклатура В (ВЫБРАТЬ вт.Номенклатура ИЗ втНомДата КАК вт)) КАК ТоварыОбороты;

////////////////////////////////////////////////////////////////////////////////
// 3. Соединяем даты с оборотами и суммируем итог
ВЫБРАТЬ
    втНомДата.Номенклатура,
    втНомДата.Дата,
    СУММА(ЕСТЬNULL(втОбороты.КоличествоОборот, 0)) КАК Остаток
ИЗ
    втНомДата КАК втНомДата
    ЛЕВОЕ СОЕДИНЕНИЕ втОбороты КАК втОбороты
    ПО втНомДата.Номенклатура = втОбороты.Номенклатура
    И втНомДата.Дата >= втОбороты.Период
СГРУППИРОВАТЬ ПО
    втНомДата.Номенклатура,
    втНомДата.Дата

Важное замечание: Рассмотрим эффективность этого метода. Если ваша база данных ведется много лет, суммирование оборотов с самого начала может быть крайне медленным. Выясним причину: СУБД приходится перебирать огромное количество записей для каждой строки исходной таблицы. Для оптимизации рекомендуется получать остаток на начало какого-то периода (например, начало текущего года) и прибавлять к нему только обороты за последний период.

Способ 3. Метод «Максимальной даты движения»

Часто остаток на конкретную дату — это просто остаток на момент последнего движения по этому товару, которое произошло ранее или в эту дату. Мы можем использовать виртуальную таблицу ОстаткиИОбороты.

Рассмотрим этот подход:

  1. Для каждой пары Номенклатура + НужнаяДата найдем в таблице ОстаткиИОбороты максимальный Период, который меньше или равен НужнойДате.
  2. Затем вторым шагом соединим полученную таблицу с этой же виртуальной таблицей ОстаткиИОбороты по найденному периоду, чтобы забрать значение остатка.

Этот способ работает быстрее суммирования, так как использует уже рассчитанные итоги регистра, но он требует аккуратной обработки «дырок» (периодов, когда движений не было вообще).

Способ 4. Программные возможности типовых конфигураций (УТ 11, ERP, КА)

Если мы работаем в современных конфигурациях, например, 1С:Управление торговлей 11, программисты 1С уже предусмотрели готовые инструменты для решения подобных задач. Вместо написания сложных запросов вручную, мы можем воспользоваться программными интерфейсами.

Проанализируем возможности модуля ЗапасыСервер. В нем существует функция ПодготовитьТаблицуСвободныхОстатков(). Она позволяет передать таблицу значений с номенклатурой и параметрами, а на выходе получить заполненные данные по остаткам. Это позволяет соблюдать стандарты разработки и гарантирует правильный учет всех нюансов (резервов, характеристик и т.д.).

Также стоит обратить внимание на объект МенеджерВременныхТаблиц. Мы можем подготовить запрос, который получает остатки на одну дату, выполнить его, выгрузить результат в ТЗ, а затем в цикле повторить это для всех уникальных дат из нашего списка. Однако помните: цикл с запросом внутри — это крайняя мера, допустимая только при очень малом количестве дат или в обработках, не критичных к скорости выполнения.

Рекомендации по выбору решения

Подведем итог и выберем оптимальный путь для вашей ситуации:

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

← На главную