При работе с регистрами накопления и регистрами бухгалтерии в платформе «1С:Предприятие» разработчики часто сталкиваются с неочевидным поведением виртуальных таблиц. Типичный пример: при получении остатков на конец дня (23:59:59) в выборку не попадают движения документов, записанные в эту же секунду. В то же время виртуальная таблица оборотов при указании того же периода успешно включает эти движения.
В этой статье мы подробно разберем, почему разработчики платформы «1С» заложили именно такую логику, как эти механизмы транслируются на уровень СУБД, к каким проблемам производительности может привести использование объектов Граница и МоментВремени, а также как эволюционировали методики контроля остатков.
Для того чтобы понять логику работы платформы, проанализируем, как именно виртуальные таблицы интерпретируют передаваемые в них параметры периодов.
Виртуальная таблица Остатки по умолчанию рассчитывает данные на начало указанной секунды (то есть не включая движения, совершенные в эту самую секунду). Если мы передаем в качестве параметра дату 31.12.2025 23:59:59, система вернет нам остатки, актуальные на самую первую миллисекунду этой секунды. Все документы, которые были записаны и проведены ровно в 23:59:59, останутся «за бортом» этого расчета.
Виртуальная таблица Обороты (а также таблица ОстаткиИОбороты) работает по иному принципу: ее параметры НачалоПериода и КонецПериода обрабатываются включительно. То есть, указывая конец периода как 23:59:59, мы явно говорим системе, что хотим получить обороты до последней миллисекунды этой секунды. В результате все движения этой секунды будут учтены.
Эта разница часто сбивает с толку начинающих разработчиков. Зачем это было сделано? Дело в математической логике: остаток — это срез состояния на конкретную временную точку (границу), в то время как оборот — это протяженный во времени интервал. Чтобы гарантировать точный расчет остатка с учетом конкретного документа, разработчикам приходится использовать специальные объекты платформы.
Чтобы управлять включением или исключением граничной секунды (или конкретного документа) при расчете остатков, в платформе «1С» предусмотрены два типа данных:
Граница — позволяет явно указать, нужно ли включать граничную точку в расчет. Для этого используется свойство ВидГраницы (значения ВидГраницы.Включая или ВидГраницы.Исключая).МоментВремени — это составной тип данных, который содержит в себе не только дату и время с точностью до секунды, но и ссылку на документ-регистратор. Это позволяет уникально идентифицировать положение документа на оси времени, даже если в одну и ту же секунду было записано несколько десятков документов.Рассмотрим пример того, как мы можем получить остатки на конец дня, гарантированно включив туда движения последней секунды с помощью объекта Граница:
КонецДняГраница = Новый Граница(КонецДня(РабочаяДата), ВидГраницы.Включая);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура,
| ТоварыНаСкладахОстатки.В НаличииОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыНаСкладахОстатки";
Запрос.УстановитьПараметр("Период", КонецДняГраница);
Результат = Запрос.Выполнить();
Казалось бы, решение найдено и оно достаточно простое. Однако на реальных высоконагруженных базах данных использование этих типов при расчете остатков в реальном времени может вызвать серьезные проблемы с производительностью.
Среди разработчиков бытует мнение, что при использовании объектов МоментВремени или Граница в запросах СУБД не может использовать индексы, так как по этим полям якобы отсутствуют индексы в базе данных. Проанализируем, как обстоят дела на самом деле.
На уровне таблиц СУБД физически не существует колонки с типом «Момент времени». Объект МоментВремени — это абстракция платформы «1С». Когда мы передаем этот объект в качестве параметра виртуальной таблицы, платформа транслирует его в SQL-запрос со сложным условным оператором OR в секции фильтрации WHERE.
СУБД вынуждена выполнять проверку следующего вида:
WHERE (Period < @ParamDate) OR (Period = @ParamDate AND RecorderRef < @ParamRef)
Именно в этой конструкции кроется главная технологическая проблема:
OR в сочетании с операторами сравнения (неравенствами) крайне тяжело оптимизируется планировщиками запросов (как в MS SQL Server, так и в PostgreSQL).Index Seek) оптимизатор СУБД очень часто принимает решение выполнить полное сканирование индекса или даже всей таблицы (Index Scan / Table Scan). Если физическая таблица движений регистра содержит миллионы записей, это приводит к колоссальной нагрузке на дисковую подсистему и процессор сервера.Помимо этого, важно понимать алгоритм расчета остатков самой платформой. База данных хранит таблицы рассчитанных итогов на начало каждого месяца. Если мы запрашиваем остаток на середину месяца, СУБД берет ближайшую сохраненную точку и выполняет «досчет» (смещение) по физической таблице движений — прибавляет движения от начала месяца или вычитает движения до конца месяца. Когда в этот досчет вмешивается условие по Моменту времени с оператором OR, производительность СУБД падает лавинообразно, вызывая длительные ожидания на блокировках.
Описанные выше проблемы производительности стали основной причиной изменения подходов фирмы «1С» к проектированию механизмов проведения документов.
Рассмотрим две ключевые методики контроля остатков, которые принципиально по-разному работают с временной осью.
В этой схеме перед записью движений система должна была удостовериться, что товара на складе достаточно. Алгоритм выглядел следующим образом:
МоментВремени проводимого документа (чтобы исключить движения самого этого документа, если они уже были ранее записаны).Минусы метода: Этот подход приводил к длинным транзакциям. Из-за использования неоптимального SQL-запроса с OR (для расчета остатка на МоментВремени) СУБД удерживала исключительные блокировки значительно дольше, чем требовалось. В условиях многопользовательской работы это приводило к постоянным взаимным блокировкам (deadlocks).
В современных конфигурациях (таких как 1С:ERP, Комплексная автоматизация, Управление торговлей, УНФ) применяется совершенно иной подход, исключающий необходимость обращения к сложным условиям по моментам времени в транзакции:
Плюсы метода: Для выполнения проверки не требуется рассчитывать остатки на середину секунды с использованием сложных параметров МоментВремени или Граница. СУБД выполняет простейший и максимально быстрый поиск по индексу (Index Seek), обращаясь напрямую к таблице актуальных итогов. Время блокировок сокращается в десятки раз, что обеспечивает высокую параллельность работы пользователей в системе.
Мы выяснили, что различие в поведении остатков и оборотов на 23:59:59 обусловлено логикой работы виртуальных таблиц. Для точечного управления этой границей существуют объекты Граница и МоментВремени. Тем не менее, использовать их в транзакциях проведения высоконагруженных регистров не рекомендуется из-за неоптимальной трансляции условий в SQL-код СУБД. Рекомендуется переходить на новую методику контроля остатков, которая использует преимущества актуальных итогов и избавляет систему от избыточных блокировок таблиц БД.