Почему конструкция ДАТАВРЕМЯ выдает ошибку в запросах 1С и как правильно решить эту проблему?

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

При написании запросов в системе «1С:Предприятие» разработчики нередко сталкиваются со специфическим поведением платформы при работе с датами. Типичная ситуация: попытка динамически сформировать дату внутри запроса или использовать расчетную дату в условиях соединения таблиц. Давайте подробно разберем, почему популярная конструкция ДАТАВРЕМЯ вызывает ошибку компиляции запроса при передаче в нее переменных или некорректных числовых значений, как решить эту проблему с помощью стандартных функций платформы и как избежать критических проблем с производительностью СУБД.

В чем причина ошибки при использовании ДАТАВРЕМЯ?

Для начала проанализируем исходный пример, который вызывает ошибку:

ЛЕВОЕ СОЕДИНЕНИЕ ТЧ ПО ТЧ.Дата = ДАТАВРЕМЯ(1, 1, 1, 0, 0, ТЧ.НПП)

При попытке выполнить такой запрос платформа выдаст ошибку: «Неверные параметры». Если попытаться заменить поле таблицы на фиксированное число, превышающее стандартные границы времени (например, 111 секунд), ошибка повторится:

ЛЕВОЕ СОЕДИНЕНИЕ ТЧ ПО ТЧ.Дата = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 111)

Выясним причину такого поведения платформы 1С. Дело в том, что конструкция ДАТАВРЕМЯ в языке запросов — это не функция, а литерал (статическая константа). Литерал обрабатывается синтаксическим анализатором 1С еще на этапе компиляции запроса (до его отправки в СУБД) и превращается в фиксированное значение типа «Дата». Из этого факта вытекают два жестких ограничения:

  1. Нельзя передавать динамические параметры. Поскольку литерал должен быть вычислен до выполнения запроса, в него невозможно передать имя поля таблицы (например, ТЧ.НПП), переменную или параметр запроса. На этапе компиляции эти значения еще неизвестны.
  2. Значения параметров должны находиться в строгих диапазонах. Параметры литерала ДАТАВРЕМЯ(Год, Месяц, День, Час, Минута, Секунда) проверяются на соответствие реальному календарю. Количество секунд должно быть строго в диапазоне от 0 до 59. Число 111 выходит за рамки допустимого значения секунд, поэтому платформа сразу блокирует выполнение запроса.

Правильное решение: использование функции ДОБАВИТЬКДАТЕ

Если нам необходимо динамически вычислить дату на стороне СУБД (например, прибавить к начальной дате произвольное количество секунд, минут или дней из таблицы БД), мы должны использовать полноценную функцию языка запросов — ДОБАВИТЬКДАТЕ.

Давайте посмотрим на пример правильного написания этого условия в запросе:


ЛЕВОЕ СОЕДИНЕНИЕ ТЧ ПО ТЧ.Дата = ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), СЕКУНДА, ТЧ.НПП)

Рассмотрим подробнее, почему этот вариант работает безошибочно:

Проблема производительности: ловушка Non-SARGable условий соединения

Несмотря на то, что использование функции ДОБАВИТЬКДАТЕ в условии соединения решает проблему синтаксиса, такое решение несет в себе скрытую угрозу производительности всей информационной системы.

Когда мы помещаем вычисление функции непосредственно в условие связи таблиц (секция ПО / ON), СУБД сталкивается с так называемым Non-SARGable условием (Search Argumentable). СУБД не может сопоставить строки таблиц, используя индексы по полю Дата, так как для каждой строки правой таблицы ей приходится «на лету» рассчитывать результат функции ДОБАВИТЬКДАТЕ. Вместо быстрого поиска по индексу (Index Seek) СУБД будет вынуждена выполнять полное сканирование таблиц (Table/Index Scan). На реальной базе данных с тысячами строк такой запрос приведет к катастрофическому падению скорости работы и блокировкам СУБД.

Оптимальное решение проблемы с помощью временных таблиц

Чтобы избежать проблем с производительностью, мы должны разделить этот запрос на шаги. Основное правило оптимизации запросов 1С: все вычисляемые поля, по которым планируется соединение, должны рассчитываться предварительно во временной таблице и индексироваться.

Разберем по шагам правильную схему реализации этого алгоритма:

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

Посмотрим на пример практической реализации этого подхода в коде запроса:


ВЫБРАТЬ
    ТЧ.Ссылка КАК ДокументСсылка,
    ТЧ.НомерСтроки КАК НомерСтроки,
    ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), СЕКУНДА, ТЧ.НПП) КАК ВычисленнаяДата
ПОМЕСТИТЬ ВТ_ПодготовленныеДанныеТЧ
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ

ИНДЕКСИРОВАТЬ ПО
    ВычисленнаяДата
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.ДокументСсылка,
    ВТ.НомерСтроки,
    ВТ.ВычисленнаяДата,
    ДругаяТаблица.Ссылка КАК НайденныйОбъект
ИЗ
    ВТ_ПодготовленныеДанныеТЧ КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВнешниеДанные КАК ДругаяТаблица
        ПО ВТ.ВычисленнаяДата = ДругаяТаблица.ДатаСобытия

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

Архитектурный антипаттерн: почему хранение чисел в датах — это ошибка проектирования

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

Выделим основные проблемы такого подхода:

Правильное архитектурное решение: добавьте в целевую таблицу (с которой происходит соединение) обычный реквизит с типом Число (например, ИдентификаторСвязи или НомерСтроки). Это позволит выполнять соединение напрямую по числовым полям без каких-либо преобразований типов:


ЛЕВОЕ СОЕДИНЕНИЕ ИсточникДанных ПО ТЧ.ИдентификаторСвязи = ИсточникДанных.ИдентификаторСвязи

Связывание таблиц по числовым ключам — это самый быстрый, надежный и прозрачный способ организации связей в любых реляционных базах данных.

← На главную