При разработке отчетов и обработок в платформе 1С:Предприятие часто возникает задача отбора документов, дата которых совпадает с одной из нескольких конкретных дат. Реализовать такое условие на языке запросов 1С можно несколькими способами. Разберем по шагам каждый из них, проанализируем нюансы производительности и выясним, как правильно работать с датами с учетом времени.
Перед тем как перейти к синтаксису условий, важно вспомнить базовое поведение платформы: значение типа Дата в 1С всегда содержит время с точностью до секунды. Если мы пишем условие точного равенства вида Документ.Дата = &ПерваяДата, платформа выберет только те документы, время которых с точностью до секунды совпадает со значением параметра (например, ровно в 00:00:00).
Если требуется отобрать документы за двое суток целиком, условие следует строить через интервалы с началом и концом каждого дня. При этом крайне не рекомендуется применять функции платформы непосредственно к полям базы данных в секции ГДЕ (например, использовать НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ)). Такое выражение делает предикат несаргабельным (non-sargable), лишая СУБД возможности использовать стандартный индекс по полю Дата, что приводит к полному сканированию всей таблицы документов.
Рассмотрим самый простой и очевидный вариант — явное указание нескольких условий, объединенных логическим оператором ИЛИ. Посмотрим на пример текста запроса:
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.Дата КАК Дата,
Реализация.Номер КАК Номер
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата = &ПерваяДата
ИЛИ Реализация.Дата = &ВтораяДата
Если же необходимо выбрать все документы за два полных дня, разберем вариант с интервалами:
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
(Реализация.Дата МЕЖДУ &НачалоДня1 И &КонецДня1)
ИЛИ (Реализация.Дата МЕЖДУ &НачалоДня2 И &КонецДня2)
Особенности оптимизации: В таблицах документов поле Дата входит первым полем в кластерный индекс. Когда в условии ИЛИ проверяется одно и то же индексированное поле, современные СУБД (MS SQL Server, PostgreSQL) строят эффективный план выполнения, выполняя точечный поиск по индексу (Index Seek). Проблемы с оператором ИЛИ обычно начинаются тогда, когда условие связывает разные поля таблицы или сложные логические выражения.
Вместо перечисления нескольких параметров можно передать в запрос единый массив нужных дат и воспользоваться оператором В. Проанализируем пример кода на встроенном языке:
МассивДат = Новый Массив;
МассивДат.Добавить(ПерваяДата);
МассивДат.Добавить(ВтораяДата);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Документ,
| Реализация.Дата КАК Дата
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата В(&МассивДат)";
Запрос.УстановитьПараметр("МассивДат", МассивДат);
Результат = Запрос.Выполнить();
Особенности оптимизации: Для фиксированного небольшого списка значений (2–10 дат) этот вариант работает максимально наглядно и транслируется СУБД в стандартную конструкцию IN (...). Однако если массив содержит сотни или тысячи элементов, платформа может сгенерировать громоздкий SQL-запрос с ограничениями по количеству параметров. Для больших динамических списков правильнее использовать временные таблицы.
В ситуациях, когда СУБД на больших объемах данных строит неоптимальный план при использовании сложных составных условий с ИЛИ, классической рекомендацией по оптимизации является разделение выборки на несколько простых запросов с последующим объединением.
Посмотрим на структуру такого запроса:
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата = &ПерваяДата
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата = &ВтораяДата
Важный нюанс: Обратите внимание на обязательное использование ключевого слова ВСЕ. Конструкция ОБЪЕДИНИТЬ без слова ВСЕ заставляет СУБД выполнять неявную сортировку и операцию DISTINCT для устранения возможных дубликатов строк, что приводит к дополнительной нагрузке на процессор и диск сервера.
Если список дат формируется динамически или передается из внешнего источника данных, наиболее надежным и масштабируемым решением будет загрузка дат во временную таблицу с ее последующим соединением с таблицей документов.
Разберем по шагам реализацию запроса с временной таблицей:
ВЫБРАТЬ
&ПерваяДата КАК ДатаОтбора
ПОМЕСТИТЬ ВТ_Даты
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
&ВтораяДата
ИНДЕКСИРОВАТЬ ПО
ДатаОтбора
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Даты КАК ВТ_Даты
ПО Реализация.Дата = ВТ_Даты.ДатаОтбора
Особенности оптимизации: Директива ИНДЕКСИРОВАТЬ ПО создает индекс по полю даты во временной таблице, что позволяет СУБД применить оптимальный алгоритм соединения (например, Merge Join или Hash Join) без дополнительных накладных расходов.
Подведем итог и выделим рекомендации для практической работы:
ИЛИ и В (&МассивДат) являются наиболее компактными, легко читаются в коде и хорошо оптимизируются современными СУБД.МЕЖДУ для начала и конца каждого дня, избегая применения функций платформы к полям в блоке ГДЕ.ИЛИ деградирует в Table Scan, применяйте конструкцию ОБЪЕДИНИТЬ ВСЕ.ВНУТРЕННЕЕ СОЕДИНЕНИЕ.