Как в запросе 1С выбрать документы по двум разным датам

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

При разработке отчетов и обработок в платформе 1С:Предприятие часто возникает задача отбора документов, дата которых совпадает с одной из нескольких конкретных дат. Реализовать такое условие на языке запросов 1С можно несколькими способами. Разберем по шагам каждый из них, проанализируем нюансы производительности и выясним, как правильно работать с датами с учетом времени.

Особенности работы с типом Дата в запросах

Перед тем как перейти к синтаксису условий, важно вспомнить базовое поведение платформы: значение типа Дата в 1С всегда содержит время с точностью до секунды. Если мы пишем условие точного равенства вида Документ.Дата = &ПерваяДата, платформа выберет только те документы, время которых с точностью до секунды совпадает со значением параметра (например, ровно в 00:00:00).

Если требуется отобрать документы за двое суток целиком, условие следует строить через интервалы с началом и концом каждого дня. При этом крайне не рекомендуется применять функции платформы непосредственно к полям базы данных в секции ГДЕ (например, использовать НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ)). Такое выражение делает предикат несаргабельным (non-sargable), лишая СУБД возможности использовать стандартный индекс по полю Дата, что приводит к полному сканированию всей таблицы документов.

Способ 1. Использование логического оператора ИЛИ

Рассмотрим самый простой и очевидный вариант — явное указание нескольких условий, объединенных логическим оператором ИЛИ. Посмотрим на пример текста запроса:


ВЫБРАТЬ
    Реализация.Ссылка КАК Документ,
    Реализация.Дата КАК Дата,
    Реализация.Номер КАК Номер
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Дата = &ПерваяДата
    ИЛИ Реализация.Дата = &ВтораяДата

Если же необходимо выбрать все документы за два полных дня, разберем вариант с интервалами:


ВЫБРАТЬ
    Реализация.Ссылка КАК Документ,
    Реализация.Дата КАК Дата
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    (Реализация.Дата МЕЖДУ &НачалоДня1 И &КонецДня1)
    ИЛИ (Реализация.Дата МЕЖДУ &НачалоДня2 И &КонецДня2)

Особенности оптимизации: В таблицах документов поле Дата входит первым полем в кластерный индекс. Когда в условии ИЛИ проверяется одно и то же индексированное поле, современные СУБД (MS SQL Server, PostgreSQL) строят эффективный план выполнения, выполняя точечный поиск по индексу (Index Seek). Проблемы с оператором ИЛИ обычно начинаются тогда, когда условие связывает разные поля таблицы или сложные логические выражения.

Способ 2. Использование оператора проверки вхождения В (IN)

Вместо перечисления нескольких параметров можно передать в запрос единый массив нужных дат и воспользоваться оператором В. Проанализируем пример кода на встроенном языке:


МассивДат = Новый Массив;
МассивДат.Добавить(ПерваяДата);
МассивДат.Добавить(ВтораяДата);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Реализация.Ссылка КАК Документ,
    |	Реализация.Дата КАК Дата
    |ИЗ
    |	Документ.РеализацияТоваровУслуг КАК Реализация
    |ГДЕ
    |	Реализация.Дата В(&МассивДат)";

Запрос.УстановитьПараметр("МассивДат", МассивДат);
Результат = Запрос.Выполнить();

Особенности оптимизации: Для фиксированного небольшого списка значений (2–10 дат) этот вариант работает максимально наглядно и транслируется СУБД в стандартную конструкцию IN (...). Однако если массив содержит сотни или тысячи элементов, платформа может сгенерировать громоздкий SQL-запрос с ограничениями по количеству параметров. Для больших динамических списков правильнее использовать временные таблицы.

Способ 3. Объединение запросов через ОБЪЕДИНИТЬ ВСЕ

В ситуациях, когда СУБД на больших объемах данных строит неоптимальный план при использовании сложных составных условий с ИЛИ, классической рекомендацией по оптимизации является разделение выборки на несколько простых запросов с последующим объединением.

Посмотрим на структуру такого запроса:


ВЫБРАТЬ
    Реализация.Ссылка КАК Документ,
    Реализация.Дата КАК Дата
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Дата = &ПерваяДата

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Реализация.Ссылка КАК Документ,
    Реализация.Дата КАК Дата
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Дата = &ВтораяДата

Важный нюанс: Обратите внимание на обязательное использование ключевого слова ВСЕ. Конструкция ОБЪЕДИНИТЬ без слова ВСЕ заставляет СУБД выполнять неявную сортировку и операцию DISTINCT для устранения возможных дубликатов строк, что приводит к дополнительной нагрузке на процессор и диск сервера.

Способ 4. Использование временной таблицы с индексацией

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

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


ВЫБРАТЬ
    &ПерваяДата КАК ДатаОтбора
ПОМЕСТИТЬ ВТ_Даты

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    &ВтораяДата

ИНДЕКСИРОВАТЬ ПО
    ДатаОтбора
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Реализация.Ссылка КАК Документ,
    Реализация.Дата КАК Дата
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Даты КАК ВТ_Даты
        ПО Реализация.Дата = ВТ_Даты.ДатаОтбора

Особенности оптимизации: Директива ИНДЕКСИРОВАТЬ ПО создает индекс по полю даты во временной таблице, что позволяет СУБД применить оптимальный алгоритм соединения (например, Merge Join или Hash Join) без дополнительных накладных расходов.

Сравнение подходов и выводы

Подведем итог и выделим рекомендации для практической работы:

  1. Для простых запросов и двух фиксированных дат: Варианты с ИЛИ и В (&МассивДат) являются наиболее компактными, легко читаются в коде и хорошо оптимизируются современными СУБД.
  2. При работе с интервалами времени: Используйте явные условия МЕЖДУ для начала и конца каждого дня, избегая применения функций платформы к полям в блоке ГДЕ.
  3. Для высоконагруженных таблиц и сложных условий: Если план выполнения запроса с ИЛИ деградирует в Table Scan, применяйте конструкцию ОБЪЕДИНИТЬ ВСЕ.
  4. Для большого динамического набора дат: Формируйте проиндексированную временную таблицу и соединяйте ее с основной таблицей документов через ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
← На главную