Вопрос по периодам виртуальных таблиц регистра: почему остатки и обороты на 23:59:59 работают по-разному?

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

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

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

1. Разница в поведении виртуальных таблиц «Остатки» и «Обороты»

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

Виртуальная таблица Остатки по умолчанию рассчитывает данные на начало указанной секунды (то есть не включая движения, совершенные в эту самую секунду). Если мы передаем в качестве параметра дату 31.12.2025 23:59:59, система вернет нам остатки, актуальные на самую первую миллисекунду этой секунды. Все документы, которые были записаны и проведены ровно в 23:59:59, останутся «за бортом» этого расчета.

Виртуальная таблица Обороты (а также таблица ОстаткиИОбороты) работает по иному принципу: ее параметры НачалоПериода и КонецПериода обрабатываются включительно. То есть, указывая конец периода как 23:59:59, мы явно говорим системе, что хотим получить обороты до последней миллисекунды этой секунды. В результате все движения этой секунды будут учтены.

Эта разница часто сбивает с толку начинающих разработчиков. Зачем это было сделано? Дело в математической логике: остаток — это срез состояния на конкретную временную точку (границу), в то время как оборот — это протяженный во времени интервал. Чтобы гарантировать точный расчет остатка с учетом конкретного документа, разработчикам приходится использовать специальные объекты платформы.

2. Работа с объектами Граница и МоментВремени

Чтобы управлять включением или исключением граничной секунды (или конкретного документа) при расчете остатков, в платформе «1С» предусмотрены два типа данных:

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

Рассмотрим пример того, как мы можем получить остатки на конец дня, гарантированно включив туда движения последней секунды с помощью объекта Граница:


КонецДняГраница = Новый Граница(КонецДня(РабочаяДата), ВидГраницы.Включая);

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

Запрос.УстановитьПараметр("Период", КонецДняГраница);
Результат = Запрос.Выполнить();

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

3. Что происходит под капотом СУБД: развенчание мифа об индексах

Среди разработчиков бытует мнение, что при использовании объектов МоментВремени или Граница в запросах СУБД не может использовать индексы, так как по этим полям якобы отсутствуют индексы в базе данных. Проанализируем, как обстоят дела на самом деле.

На уровне таблиц СУБД физически не существует колонки с типом «Момент времени». Объект МоментВремени — это абстракция платформы «1С». Когда мы передаем этот объект в качестве параметра виртуальной таблицы, платформа транслирует его в SQL-запрос со сложным условным оператором OR в секции фильтрации WHERE.

СУБД вынуждена выполнять проверку следующего вида:

WHERE (Period < @ParamDate) OR (Period = @ParamDate AND RecorderRef < @ParamRef)

Именно в этой конструкции кроется главная технологическая проблема:

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

4. Эволюция контроля остатков: «Старая» и «Новая» методики

Описанные выше проблемы производительности стали основной причиной изменения подходов фирмы «1С» к проектированию механизмов проведения документов.

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

«Старый» метод (Контроль остатков при помощи Момента времени)

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

  1. Документ начинает транзакцию проведения.
  2. Выполняется запрос к виртуальной таблице остатков на МоментВремени проводимого документа (чтобы исключить движения самого этого документа, если они уже были ранее записаны).
  3. Если остаток товара достаточен, в базу данных записываются новые движения.
  4. Транзакция завершается (фиксируется).

Минусы метода: Этот подход приводил к длинным транзакциям. Из-за использования неоптимального SQL-запроса с OR (для расчета остатка на МоментВремени) СУБД удерживала исключительные блокировки значительно дольше, чем требовалось. В условиях многопользовательской работы это приводило к постоянным взаимным блокировкам (deadlocks).

«Новый» метод (Оперативный контроль остатков)

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

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

Плюсы метода: Для выполнения проверки не требуется рассчитывать остатки на середину секунды с использованием сложных параметров МоментВремени или Граница. СУБД выполняет простейший и максимально быстрый поиск по индексу (Index Seek), обращаясь напрямую к таблице актуальных итогов. Время блокировок сокращается в десятки раз, что обеспечивает высокую параллельность работы пользователей в системе.

Резюме

Мы выяснили, что различие в поведении остатков и оборотов на 23:59:59 обусловлено логикой работы виртуальных таблиц. Для точечного управления этой границей существуют объекты Граница и МоментВремени. Тем не менее, использовать их в транзакциях проведения высоконагруженных регистров не рекомендуется из-за неоптимальной трансляции условий в SQL-код СУБД. Рекомендуется переходить на новую методику контроля остатков, которая использует преимущества актуальных итогов и избавляет систему от избыточных блокировок таблиц БД.

← На главную