Как в запросе 1С создать или имитировать момент времени для сравнения данных

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

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

Что такое Момент времени с точки зрения платформы и базы данных

Прежде чем мы перейдем к решению, давайте проанализируем, что именно представляет собой МоментВремени. В системе 1С это не примитивный тип данных, а объект-контейнер, который включает в себя два компонента:

  1. Дата (с точностью до секунды).
  2. Ссылка (уникальный идентификатор GUID объекта, создавшего запись).

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

В языке запросов 1С существует ключевое слово МОМЕНТ ВРЕМЕНИ, но оно применимо исключительно к полю ссылки. Например, ДокументСсылка.МоментВремени. Попытка сконструировать этот объект из произвольной даты и ссылки прямо в тексте запроса (как мы делаем это в коде через Новый МоментВремени(...)) невозможна — в языке запросов просто нет соответствующего конструктора.

Проблема: Сравнение с «будущим» моментом

Рассмотрим ситуацию, описанную в теме: нам нужно сравнить данные в последовательности с неким расчетным моментом, который еще не записан в базу. Например, дата документа — 1 января, а дата проводок — 31 января. Нам нужно получить данные из последовательности, которые находятся «после» момента (31.01.2000 + СсылкаНаДокумент).

Выясним причину, по которой обычное сравнение дат здесь не сработает. Если мы будем сравнивать только по дате, мы потеряем точность внутри одной секунды. Если мы попытаемся использовать Граница, то столкнемся с тем, что этот объект можно передать только как параметр, но нельзя сформировать внутри запроса на основе данных из временных таблиц.

Решение: Логическая декомпозиция момента времени

Единственный надежный способ имитировать сравнение с моментом времени в запросе — это разложить объект на составляющие и использовать составное условие. Разберем этот алгоритм подробно.

Чтобы найти все записи, которые по моменту времени следуют строго после нашего расчетного момента (состоящего из &НашаДата и &НашаСсылка), нам необходимо описать два случая:

  1. Записи, у которых дата просто больше нашей расчетной даты.
  2. Записи, у которых дата совпадает с нашей, но ссылка (GUID) «старше» нашей ссылки.

Проанализируем пример кода для такого условия в секции ГДЕ:


ГДЕ 
    ТаблицаПоследовательности.Период > &РасчетнаяДата
    ИЛИ (ТаблицаПоследовательности.Период = &РасчетнаяДата 
         И ТаблицаПоследовательности.Регистратор > &РасчетнаяСсылка)

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

Реализация через временные таблицы

Если расчетная дата и ссылка не являются внешними параметрами, а вычисляются внутри самого запроса, нам необходимо использовать временные таблицы. Рассмотрим по шагам, как это реализовать.

Шаг 1: Подготовка исходных данных. Сначала мы выбираем необходимые нам данные (дату проводок и ссылку) во временную таблицу.


ВЫБРАТЬ
    ДокументОбъект.Ссылка КАК СсылкаДляСравнения,
    ДокументОбъект.ДатаПроводок КАК ДатаДляСравнения
ПОМЕСТИТЬ ВТ_ЦелевойМомент
ИЗ
    Документ.НашДокумент КАК ДокументОбъект
ГДЕ
    ДокументОбъект.Ссылка = &СсылкаНаТекущийДокумент
;

Шаг 2: Соединение и фильтрация. Теперь мы выбираем данные из последовательности (или регистра), соединяя их с нашей временной таблицей, и применяем логику сравнения моментов.


ВЫБРАТЬ
    Реестр.Регистратор,
    Реестр.Период
ИЗ
    Последовательность.НашаПоследовательность КАК Реестр
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ЦелевойМомент КАК Цель
        ПО (Реестр.Период > Цель.ДатаДляСравнения)
            ИЛИ (Реестр.Период = Цель.ДатаДляСравнения 
                 И Реестр.Регистратор > Цель.СсылкаДляСравнения)

Особенности работы с Границей (Boundary)

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

Посмотрим на пример имитации границы «Включая» в запросе:


ГДЕ 
    Таблица.Период > &Дата
    ИЛИ (Таблица.Период = &Дата И Таблица.Регистратор >= &Ссылка)

Производительность и индексация

Многих разработчиков пугает использование оператора ИЛИ в условиях запроса, так как это может привести к полному сканированию таблицы (Table Scan). Однако в данном конкретном случае ситуация иная. Большинство таблиц в 1С (документы, регистры сведений, таблицы последовательностей) имеют кластерный индекс или мощные дополнительные индексы именно по полям Период + Ссылка (или Дата + Ссылка).

Современные оптимизаторы СУБД (MS SQL Server, PostgreSQL) отлично справляются с конструкцией (Дата > X) OR (Дата = X AND Ссылка > Y). Они используют поиск по индексу (Index Seek), что делает такой запрос максимально быстрым даже на таблицах с миллионами записей.

Подведем итоги

Мы выяснили, что напрямую создать тип МоментВремени в запросе нельзя, но это и не требуется. Для решения задачи достаточно следовать алгоритму:

  1. Определить составляющие момента: Дату и Ссылку.
  2. Если данные уже есть в базе, получить их во временную таблицу.
  3. Использовать логическую конструкцию сравнения по двум полям.
  4. Учитывать вид границы (включая/исключая) через правильный выбор операторов сравнения.

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

← На главную