В практике разработки на платформе 1С:Предприятие часто возникают ситуации, когда стандартного функционала работы с датами становится недостаточно. Одной из таких задач является оперирование понятием Момент времени внутри запроса, особенно когда этот момент не совпадает с реальной датой документа, а вычисляется на лету (например, на основе даты проводок или другого реквизита). В этой статье мы подробно разберем, почему нельзя просто «создать» момент времени в запросе и какими методами можно эффективно имитировать это поведение для решения прикладных задач, связанных с последовательностями и регистрами.
Прежде чем мы перейдем к решению, давайте проанализируем, что именно представляет собой МоментВремени. В системе 1С это не примитивный тип данных, а объект-контейнер, который включает в себя два компонента:
Такая структура необходима для однозначного позиционирования документов в хронологическом ряду. Поскольку в одну и ту же секунду может быть записано несколько документов, именно ссылка (GUID) выступает в роли «арбитра», определяющего, какой документ считается идущим раньше, а какой — позже.
В языке запросов 1С существует ключевое слово МОМЕНТ ВРЕМЕНИ, но оно применимо исключительно к полю ссылки. Например, ДокументСсылка.МоментВремени. Попытка сконструировать этот объект из произвольной даты и ссылки прямо в тексте запроса (как мы делаем это в коде через Новый МоментВремени(...)) невозможна — в языке запросов просто нет соответствующего конструктора.
Рассмотрим ситуацию, описанную в теме: нам нужно сравнить данные в последовательности с неким расчетным моментом, который еще не записан в базу. Например, дата документа — 1 января, а дата проводок — 31 января. Нам нужно получить данные из последовательности, которые находятся «после» момента (31.01.2000 + СсылкаНаДокумент).
Выясним причину, по которой обычное сравнение дат здесь не сработает. Если мы будем сравнивать только по дате, мы потеряем точность внутри одной секунды. Если мы попытаемся использовать Граница, то столкнемся с тем, что этот объект можно передать только как параметр, но нельзя сформировать внутри запроса на основе данных из временных таблиц.
Единственный надежный способ имитировать сравнение с моментом времени в запросе — это разложить объект на составляющие и использовать составное условие. Разберем этот алгоритм подробно.
Чтобы найти все записи, которые по моменту времени следуют строго после нашего расчетного момента (состоящего из &НашаДата и &НашаСсылка), нам необходимо описать два случая:
Проанализируем пример кода для такого условия в секции ГДЕ:
ГДЕ
ТаблицаПоследовательности.Период > &РасчетнаяДата
ИЛИ (ТаблицаПоследовательности.Период = &РасчетнаяДата
И ТаблицаПоследовательности.Регистратор > &РасчетнаяСсылка)
Важный нюанс: Платформа 1С позволяет сравнивать ссылочные типы в запросах с помощью операторов больше/меньше. Это сравнение происходит на уровне СУБД по бинарному представлению GUID. Это именно то, что делает система внутри себя, когда вы используете штатные механизмы позиционирования.
Если расчетная дата и ссылка не являются внешними параметрами, а вычисляются внутри самого запроса, нам необходимо использовать временные таблицы. Рассмотрим по шагам, как это реализовать.
Шаг 1: Подготовка исходных данных. Сначала мы выбираем необходимые нам данные (дату проводок и ссылку) во временную таблицу.
ВЫБРАТЬ
ДокументОбъект.Ссылка КАК СсылкаДляСравнения,
ДокументОбъект.ДатаПроводок КАК ДатаДляСравнения
ПОМЕСТИТЬ ВТ_ЦелевойМомент
ИЗ
Документ.НашДокумент КАК ДокументОбъект
ГДЕ
ДокументОбъект.Ссылка = &СсылкаНаТекущийДокумент
;
Шаг 2: Соединение и фильтрация. Теперь мы выбираем данные из последовательности (или регистра), соединяя их с нашей временной таблицей, и применяем логику сравнения моментов.
ВЫБРАТЬ
Реестр.Регистратор,
Реестр.Период
ИЗ
Последовательность.НашаПоследовательность КАК Реестр
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ЦелевойМомент КАК Цель
ПО (Реестр.Период > Цель.ДатаДляСравнения)
ИЛИ (Реестр.Период = Цель.ДатаДляСравнения
И Реестр.Регистратор > Цель.СсылкаДляСравнения)
В сообщениях форума часто упоминается объект Граница. Стоит помнить, что Граница — это расширение момента времени, которое определяет, включается ли сам момент в выборку (ВидГраницы.Включая) или нет (ВидГраницы.Исключая). В запросе это реализуется простой сменой знаков сравнения:
>.>= в части сравнения ссылок при равных датах.Посмотрим на пример имитации границы «Включая» в запросе:
ГДЕ
Таблица.Период > &Дата
ИЛИ (Таблица.Период = &Дата И Таблица.Регистратор >= &Ссылка)
Многих разработчиков пугает использование оператора ИЛИ в условиях запроса, так как это может привести к полному сканированию таблицы (Table Scan). Однако в данном конкретном случае ситуация иная. Большинство таблиц в 1С (документы, регистры сведений, таблицы последовательностей) имеют кластерный индекс или мощные дополнительные индексы именно по полям Период + Ссылка (или Дата + Ссылка).
Современные оптимизаторы СУБД (MS SQL Server, PostgreSQL) отлично справляются с конструкцией (Дата > X) OR (Дата = X AND Ссылка > Y). Они используют поиск по индексу (Index Seek), что делает такой запрос максимально быстрым даже на таблицах с миллионами записей.
Мы выяснили, что напрямую создать тип МоментВремени в запросе нельзя, но это и не требуется. Для решения задачи достаточно следовать алгоритму:
Этот подход универсален и применим как для обычных, так и для управляемых форм, обеспечивая точность позиционирования данных в любых учетных механизмах платформы 1С.