При разработке отчетов и обработок в системе 1С часто возникает ситуация, когда логика выбора данных зависит от заполненности тех или иных полей. Одной из классических задач является выбор «актуальной» даты: например, если заполнена специальная дата (дата корректировки или разделения), мы должны использовать её, а если нет — стандартную дату документа или ввода. В этой статье мы подробно разберем, как реализовать такую проверку в запросе и, что более важно, как правильно наложить фильтр на полученный результат.
Для начала разберем базовую задачу: как в списке выбора ВЫБРАТЬ указать условие. В языке запросов 1С для этого используется оператор ВЫБОР. Важно помнить, что «пустая» дата в 1С — это не NULL (хотя NULL тоже может возникнуть при соединениях), а специальное значение ДАТАВРЕМЯ(1, 1, 1).
Рассмотрим пример, где нам нужно выбрать дату. Если поле ДатаРазделения заполнено, берем его, иначе — ДатаВвода. Напишем конструкцию:
ВЫБОР
КОГДА СправочникОС.ДатаРазделения = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА СправочникОС.ДатаВвода
ИНАЧЕ СправочникОС.ДатаРазделения
КОНЕЦ КАК ВыбДата
В этом коде мы проверяем ДатаРазделения на равенство пустой дате. Если условие истинно, система возвращает значение из второго поля. Обратите внимание на использование псевдонима КАК ВыбДата — он пригодится нам для отображения результата в отчете.
Часто программисты пытаются использовать созданный псевдоним ВыбДата прямо в условии фильтрации, например: ГДЕ ВыбДата МЕЖДУ &ДатаНач И &ДатаКон. Однако при попытке выполнить такой запрос система выдаст ошибку: «Поле не найдено "ВыбДата"».
Выясним причину этой ошибки. Согласно логике исполнения SQL-запроса (на котором базируется язык запросов 1С), секция ГДЕ обрабатывается базой данных раньше, чем формируются поля в секции ВЫБРАТЬ. На момент наложения фильтра система еще ничего не знает о существовании «поля» ВыбДата.
Проанализируем два основных способа решения этой проблемы:
ВЫБОР непосредственно в секции ГДЕ.Это самый быстрый способ, если запрос небольшой. Мы просто копируем логику выбора в параметры фильтрации МЕЖДУ. Рассмотрим, как это выглядит на практике:
ГДЕ
ВЫБОР
КОГДА СправочникОС.ДатаРазделения = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА СправочникОС.ДатаВвода
ИНАЧЕ СправочникОС.ДатаРазделения
КОНЕЦ МЕЖДУ &ДатаНач И &ДатаКон
Несмотря на кажущуюся избыточность, этот метод отлично работает. Однако у него есть существенный минус — производительность. Когда мы используем ВЫБОР внутри ГДЕ, СУБД часто не может использовать индексы по полям дат (это называется «non-SARGable» условие), что может привести к замедлению работы на больших таблицах.
Для повышения читаемости и оптимизации запроса лучше всего сначала подготовить данные во временной таблице, а затем выбрать их с нужным условием. Разберем по шагам:
Шаг 1. Создаем временную таблицу, в которой вычисляем нужную дату:
ВЫБРАТЬ
ОС.Ссылка КАК Ссылка,
ВЫБОР
КОГДА ОС.ДатаРазделения = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ОС.ДатаВвода
ИНАЧЕ ОС.ДатаРазделения
КОНЕЦ КАК АктуальнаяДата
ПОМЕСТИТЬ ВТ_ДанныеОС
ИЗ
Справочник.ОсновныеСредства КАК ОС
;
Шаг 2. Выбираем данные из временной таблицы, где поле АктуальнаяДата уже существует и по нему можно легко фильтровать:
ВЫБРАТЬ
ВТ.Ссылка,
ВТ.АктуальнаяДата
ИЗ
ВТ_ДанныеОС КАК ВТ
ГДЕ
ВТ.АктуальнаяДата МЕЖДУ &ДатаНач И &ДатаКон
В сложных запросах, например, при работе с регистром бухгалтерии, поле Субконто имеет составной тип. Если мы обращаемся к реквизиту через точку (например, Субконто1.ДатаРазделения), система выполняет неявное соединение со всеми таблицами, входящими в этот тип. Это может катастрофически снизить скорость выполнения.
Посмотрим на пример правильной типизации с помощью функции ВЫРАЗИТЬ. Это «подсказывает» системе, к какой именно таблице нужно обращаться:
ВЫБОР
КОГДА ВЫРАЗИТЬ(УправленческийОстатки.Субконто1 КАК Справочник.ОсновныеСредства).ДатаРазделения = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ВЫРАЗИТЬ(УправленческийОстатки.Субконто1 КАК Справочник.ОсновныеСредства).ДатаВвода
ИНАЧЕ ВЫРАЗИТЬ(УправленческийОстатки.Субконто1 КАК Справочник.ОсновныеСредства).ДатаРазделения
КОНЕЦ
Использование ВЫРАЗИТЬ гарантирует, что запрос не будет пытаться искать поле ДатаРазделения в справочниках «Контрагенты» или «Номенклатура», если они также входят в состав Субконто1.
При использовании ЛЕВОЕ СОЕДИНЕНИЕ поле даты может содержать NULL. Помните, что NULL не равен ДАТАВРЕМЯ(1, 1, 1). Если вы ожидаете, что дата может отсутствовать вовсе (запись не найдена в присоединенной таблице), используйте функцию ЕСТЬNULL.
Проанализируем пример совмещения проверок:
ВЫБОР
КОГДА ЕСТЬNULL(ДопСведения.ДатаСобытия, ДАТАВРЕМЯ(1, 1, 1)) = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА Документ.Дата
ИНАЧЕ ДопСведения.ДатаСобытия
КОНЕЦ
Подведем итоги: для корректной проверки заполнения даты используйте ВЫБОР с проверкой на ДАТАВРЕМЯ(1, 1, 1). Если результат нужно использовать в фильтре, выносите логику во временную таблицу или дублируйте ВЫБОР в секции ГДЕ. Всегда используйте ВЫРАЗИТЬ для полей составного типа, чтобы ваш код работал быстро и стабильно.