При написании запросов в системе «1С:Предприятие» разработчики нередко сталкиваются со специфическим поведением платформы при работе с датами. Типичная ситуация: попытка динамически сформировать дату внутри запроса или использовать расчетную дату в условиях соединения таблиц. Давайте подробно разберем, почему популярная конструкция ДАТАВРЕМЯ вызывает ошибку компиляции запроса при передаче в нее переменных или некорректных числовых значений, как решить эту проблему с помощью стандартных функций платформы и как избежать критических проблем с производительностью СУБД.
Для начала проанализируем исходный пример, который вызывает ошибку:
ЛЕВОЕ СОЕДИНЕНИЕ ТЧ ПО ТЧ.Дата = ДАТАВРЕМЯ(1, 1, 1, 0, 0, ТЧ.НПП)
При попытке выполнить такой запрос платформа выдаст ошибку: «Неверные параметры». Если попытаться заменить поле таблицы на фиксированное число, превышающее стандартные границы времени (например, 111 секунд), ошибка повторится:
ЛЕВОЕ СОЕДИНЕНИЕ ТЧ ПО ТЧ.Дата = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 111)
Выясним причину такого поведения платформы 1С. Дело в том, что конструкция ДАТАВРЕМЯ в языке запросов — это не функция, а литерал (статическая константа). Литерал обрабатывается синтаксическим анализатором 1С еще на этапе компиляции запроса (до его отправки в СУБД) и превращается в фиксированное значение типа «Дата». Из этого факта вытекают два жестких ограничения:
ТЧ.НПП), переменную или параметр запроса. На этапе компиляции эти значения еще неизвестны.ДАТАВРЕМЯ(Год, Месяц, День, Час, Минута, Секунда) проверяются на соответствие реальному календарю. Количество секунд должно быть строго в диапазоне от 0 до 59. Число 111 выходит за рамки допустимого значения секунд, поэтому платформа сразу блокирует выполнение запроса.Если нам необходимо динамически вычислить дату на стороне СУБД (например, прибавить к начальной дате произвольное количество секунд, минут или дней из таблицы БД), мы должны использовать полноценную функцию языка запросов — ДОБАВИТЬКДАТЕ.
Давайте посмотрим на пример правильного написания этого условия в запросе:
ЛЕВОЕ СОЕДИНЕНИЕ ТЧ ПО ТЧ.Дата = ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), СЕКУНДА, ТЧ.НПП)
Рассмотрим подробнее, почему этот вариант работает безошибочно:
ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0). Он абсолютно корректен, так как все его параметры находятся в допустимых границах.ДОБАВИТЬКДАТЕ транслируется платформой 1С в соответствующую функцию SQL-сервера (например, DATEADD в MS SQL Server). Это полноценная функция СУБД, которая выполняется во время работы запроса и умеет оперировать динамическими полями (такими как ТЧ.НПП).ТЧ.НПП содержит число 4000 (секунд), СУБД прибавит их к исходной дате и автоматически пересчитает результат в 1 час, 6 минут и 40 секунд. Результатом выражения станет корректная дата: 01.01.0001 01:06:40.Несмотря на то, что использование функции ДОБАВИТЬКДАТЕ в условии соединения решает проблему синтаксиса, такое решение несет в себе скрытую угрозу производительности всей информационной системы.
Когда мы помещаем вычисление функции непосредственно в условие связи таблиц (секция ПО / ON), СУБД сталкивается с так называемым Non-SARGable условием (Search Argumentable). СУБД не может сопоставить строки таблиц, используя индексы по полю Дата, так как для каждой строки правой таблицы ей приходится «на лету» рассчитывать результат функции ДОБАВИТЬКДАТЕ. Вместо быстрого поиска по индексу (Index Seek) СУБД будет вынуждена выполнять полное сканирование таблиц (Table/Index Scan). На реальной базе данных с тысячами строк такой запрос приведет к катастрофическому падению скорости работы и блокировкам СУБД.
Чтобы избежать проблем с производительностью, мы должны разделить этот запрос на шаги. Основное правило оптимизации запросов 1С: все вычисляемые поля, по которым планируется соединение, должны рассчитываться предварительно во временной таблице и индексироваться.
Разберем по шагам правильную схему реализации этого алгоритма:
Посмотрим на пример практической реализации этого подхода в коде запроса:
ВЫБРАТЬ
ТЧ.Ссылка КАК ДокументСсылка,
ТЧ.НомерСтроки КАК НомерСтроки,
ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), СЕКУНДА, ТЧ.НПП) КАК ВычисленнаяДата
ПОМЕСТИТЬ ВТ_ПодготовленныеДанныеТЧ
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
ИНДЕКСИРОВАТЬ ПО
ВычисленнаяДата
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.ДокументСсылка,
ВТ.НомерСтроки,
ВТ.ВычисленнаяДата,
ДругаяТаблица.Ссылка КАК НайденныйОбъект
ИЗ
ВТ_ПодготовленныеДанныеТЧ КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВнешниеДанные КАК ДругаяТаблица
ПО ВТ.ВычисленнаяДата = ДругаяТаблица.ДатаСобытия
Благодаря индексации поля ВычисленнаяДата во временной таблице ВТ_ПодготовленныеДанныеТЧ, СУБД мгновенно выполнит операцию левого соединения, используя индексный поиск. Это обеспечит стабильно высокую скорость работы системы даже при обработке документов, содержащих десятки тысяч строк.
Проанализируем первопричину задачи, озвученную автором темы: «В поле дата сохранено число. Нужно найти запись с этим числом и получить остальные поля». Попытка использовать тип данных Дата для хранения порядкового номера строки, идентификатора или любого другого целочисленного значения является грубым архитектурным антипаттерном.
Выделим основные проблемы такого подхода:
Число с нулевой точностью).31.12.3999 23:59:59. Если числовое значение перевалит за количество секунд, эквивалентное этой дате, программа выдаст критическую ошибку переполнения при записи данных, что заблокирует работу пользователей.Правильное архитектурное решение: добавьте в целевую таблицу (с которой происходит соединение) обычный реквизит с типом Число (например, ИдентификаторСвязи или НомерСтроки). Это позволит выполнять соединение напрямую по числовым полям без каких-либо преобразований типов:
ЛЕВОЕ СОЕДИНЕНИЕ ИсточникДанных ПО ТЧ.ИдентификаторСвязи = ИсточникДанных.ИдентификаторСвязи
Связывание таблиц по числовым ключам — это самый быстрый, надежный и прозрачный способ организации связей в любых реляционных базах данных.