Почему виртуальная таблица ОстаткиИОбороты возвращает разные остатки с периодичностью и без?

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

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

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

Анализируем исходную ситуацию и два запроса

Рассмотрим практический пример, с которым столкнулся автор темы. Предположим, у нас есть регистр накопления ТоварыНаСкладах. Мы хотим проанализировать остатки товаров на складах. Для этого мы пишем два запроса.

Запрос №1 (с периодичностью «Регистратор»):

В этом запросе мы указываем периодичность Регистратор в третьем параметре виртуальной таблицы и выбираем поле регистратора в списке выбора:


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

Этот запрос успешно возвращает нам детальную таблицу, в которой присутствуют документы-регистраторы и положительные конечные остатки товаров на момент проведения каждого документа.

Запрос №2 (без указания периодичности):

Во втором запросе мы убираем периодичность из параметров виртуальной таблицы и не выбираем поле регистратора:


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

К нашему удивлению, этот запрос либо возвращает пустой результат, либо выводит строки с нулевыми значениями остатков. Выясним, почему так происходит и как платформа 1С обрабатывает эти запросы на уровне базы данных.

Разница в механизмах расчета: почему результаты отличаются?

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

Как работает запрос без указания периодичности (Запрос №2):

Когда параметры периодичности в виртуальной таблице отсутствуют, платформа рассчитывает остатки исключительно на границы указанного интервала. Поскольку параметры периода (НачалоПериода и КонецПериода) в примере оставлены пустыми, платформа рассчитывает остатки на самый конец времени — то есть на текущий момент (используя таблицу оперативных итогов регистра).

Давайте разберем жизненный цикл товара на складе:

  1. 01.10.2023 поступил товар «Смартфон» в количестве 10 шт. (Остаток стал равен 10).
  2. 15.10.2023 весь товар «Смартфон» в количестве 10 шт. был продан. (Остаток стал равен 0).

Если мы выполняем Запрос №2 (без периодичности) на текущую дату (например, в ноябре 2023 года), система видит, что конечный остаток товара равен 0. Поскольку оборотов за весь период (если он не ограничен) на текущий момент больше нет, а итоговый остаток равен нулю, СУБД просто не возвращает данную строку (или возвращает 0, если наложены определенные условия). Это абсолютно корректное поведение: на конец запрашиваемого периода товара на складе действительно нет.

Как работает запрос с периодичностью «Регистратор» (Запрос №1):

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

Вернемся к нашему примеру со смартфоном. При указании периодичности Регистратор виртуальная таблица разобьет данные на две точки:

  1. На момент документа «Приходная накладная от 01.10.2023» — в этой точке конечный остаток составлял 10 шт.
  2. На момент документа «Расходная накладная от 15.10.2023» — в этой точке конечный остаток стал равен 0 шт.

Первый запрос выводит обе эти точки. Именно поэтому в результате Запроса №1 мы видим строку с приходным документом и положительным остатком 10 шт., несмотря на то, что к сегодняшнему дню этот остаток уже равен нулю. Мы видим историю изменения остатков, привязанную к документам движения.

Важные технические нюансы и правила работы с виртуальной таблицей

Для того чтобы правильно проектировать запросы к виртуальной таблице ОстаткиИОбороты и избегать проблем с производительностью или неверным отображением данных, рассмотрим несколько фундаментальных правил платформы 1С, о которых важно знать каждому разработчику.

1. Правило парности полей остатков

При использовании виртуальной таблицы ОстаткиИОбороты с детализацией по периодам существует важная оптимизационная особенность платформы. Если вы выбираете периодичность (например, Регистратор, День, Месяц), настоятельно рекомендуется выбирать в запросе поля остатков парами — то есть одновременно и НачальныйОстаток, и КонечныйОстаток. Также рекомендуется выбирать поля оборотов (Приход и Расход).

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

2. Обязательное указание родительских полей периодов

Поле Регистратор является подчиненным по отношению к периоду времени (секунде). Согласно официальным стандартам разработки фирмы «1С», при использовании периодичности Регистратор в списке выбора запроса обязательно должно присутствовать поле Период (или его более точные аналоги, например, ПериодСекунда).

Без родительского поля периода СУБД не сможет гарантировать строгое хронологическое упорядочивание документов в результирующей выборке, особенно если несколько документов были проведены в одну и ту же секунду. В результате промежуточные нарастающие остатки могут рассчитаться неверно («поплыть»).

Рассмотрим правильный пример построения такого запроса:


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

3. Преимущество использования периодичности «Авто»

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

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

4. Проблема «проплешин» в остатках и метод дополнения периодов

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

Для управления этим поведением у виртуальной таблицы существует четвертый параметр — МетодДополнения. Он может принимать два значения:

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

Если перед вами стоит задача получить остатки на абсолютно каждый день (включая те дни, когда движения не совершались), стандартного метода дополнения может не хватить. В таких случаях разработчики используют механизм дополнения периодов в СКД (на вкладке «Настройки» для полей периода) либо связывают таблицу остатков левым соединением с таблицей дат (производственным календарем или сгенерированной временной таблицей последовательных дат).

Резюме

Разница в результатах запросов автора темы обусловлена разным логическим смыслом параметров виртуальной таблицы. Запрос без указания периодичности возвращает срез остатков на одну конкретную точку — конец периода (где остаток равен 0). Запрос с периодичностью Регистратор разворачивает всю историю движений и показывает остатки нарастающим итогом на момент совершения каждой операции. При проектировании отчетов всегда учитывайте эти особенности работы СУБД, используйте периодичность Авто и не забывайте выбирать поля остатков парами вместе с полями периодов.

← На главную