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

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

При разработке отчетов и запросов в системе «1С:Предприятие 8» часто возникает задача ограничить выборку данных динамическим периодом. Типичный пример: нам нужно вывести записи, начиная с определенной даты, но при этом ограничить их либо концом выбранного пользователем периода, либо текущей датой (в зависимости от того, что наступит раньше). Поскольку в языке запросов 1С отсутствуют встроенные функции типа Мин() или Макс() для обычных полей, решение требует особого подхода к синтаксису.

В этой статье мы подробно разберем, как реализовать подобную логику, рассмотрим различные варианты написания условий в тексте запроса и в системе компоновки данных (СКД), а также проанализируем вопросы производительности.

Способ 1. Использование оператора ВЫБОР в тексте запроса

Наиболее очевидный способ реализовать условие «если одна дата меньше другой, то берем ее» — это использование конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ. Рассмотрим, как это выглядит в коде запроса. Допустим, у нас есть параметр &ДатаОкончания и параметр &ТекущаяДата.

Разберем пример реализации условия:


ГДЕ
    Период >= &ДатаНачала
    И Период <= ВЫБОР
        КОГДА &ДатаОкончания < &ТекущаяДата
            ТОГДА &ДатаОкончания
        ИНАЧЕ &ТекущаяДата
    КОНЕЦ

В данном фрагменте мы просим систему сначала сравнить два параметра. Если дата окончания периода, указанная пользователем, меньше текущего момента, запрос подставит её в качестве границы. В противном случае границей станет текущая дата. Это гибкое решение, которое позволяет динамически менять логику фильтрации прямо «на лету».

Способ 2. Оптимизированное логическое условие

Рассмотрим ситуацию с точки зрения производительности СУБД. Использование конструкции ВЫБОР внутри секции ГДЕ иногда может мешать оптимизатору SQL правильно использовать индексы таблицы (так называемая проблема SARGability). Если база данных огромная, это может привести к замедлению выполнения запроса.

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


ГДЕ
    Период >= &ДатаНачала
    И Период <= &ДатаОкончания
    И Период <= &ТекущаяДата

Почему это работает? Если мы накладываем два условия «меньше или равно» одновременно, система автоматически отсечет данные по той дате, которая наступит раньше. Это решение является наиболее предпочтительным для высоконагруженных систем, так как оно максимально прозрачно для SQL-сервера и позволяет эффективно использовать индекс по полю Период.

Способ 3. Реализация через выражения параметров в СКД

Если вы работаете с Системой компоновки данных (СКД), вам необязательно усложнять текст запроса. Мы можем вынести логику расчета «правильной» даты окончания на уровень параметров компоновки. Это позволяет оставить текст запроса чистым: ГДЕ Период МЕЖДУ &ДатаНачала И &РезультирующаяДата.

Выполним настройку по шагам:

  1. Откроем вкладку Параметры в схеме компоновки данных.
  2. Найдем параметр (или создадим новый), который будет отвечать за финальную дату (например, ДатаОкончанияДляЗапроса).
  3. В колонке Выражение напишем программный код:
    ВЫБОР КОГДА &КонецПериода < ТекущаяДата() ТОГДА &КонецПериода ИНАЧЕ ТекущаяДата() КОНЕЦ
  4. Для параметра КонецПериода (который заполняет пользователь) можно установить ограничение доступности, чтобы он служил только «входом» для нашей формулы.

Этот метод удобен тем, что в самом запросе нам не нужно думать о сравнении дат — мы передаем в него уже готовое, вычисленное значение.

Способ 4. Особенности работы с виртуальными таблицами

Важно помнить, что если ваш запрос обращается к виртуальным таблицам (например, ОстаткиИОбороты, СрезПоследних или Обороты), то условие на дату категорически нельзя ставить в секции ГДЕ. Это приведет к тому, что система сначала получит данные за все время, а потом начнет их фильтровать, что крайне неэффективно.

Проанализируем правильный подход: расчетную дату нужно передавать в параметры самой виртуальной таблицы. Например:


ВЫБРАТЬ
    ПродажиОбороты.Номенклатура,
    ПродажиОбороты.КоличествоОборот
ИЗ
    РегистрНакопления.Продажи.Обороты(&ДатаНачала, &РезультирующаяДата, , ) КАК ПродажиОбороты

Здесь &РезультирующаяДата должна быть вычислена заранее либо в СКД (как описано выше), либо в коде на встроенном языке перед запуском запроса.

Важные нюансы: время и пустые значения

Разберем несколько критических моментов, которые часто упускают начинающие разработчики:

  1. Граница дня: Функция ТекущаяДата() возвращает время вплоть до секунды. Если ваш период — это «конец дня», а текущее время — обед, вы можете потерять часть данных за сегодняшний день. Рекомендуется использовать функцию КОНЕЦПЕРИОДА, чтобы привести сравнение к логическому завершению дня.
  2. Пустые даты: Если параметр даты не заполнен, 1С воспринимает его как 01.01.0001. При сравнении «меньше или равно» пустая дата может испортить всю логику. Всегда проверяйте заполнение параметров или используйте конструкцию ЕСТЬNULL, если данные берутся из таблиц.
  3. Преобразование в коде: Если вы выполняете запрос не в СКД, а в обычном модуле, проще всего вычислить дату до формирования запроса:
    
    КонечнаяДата = Мин(Объект.ДатаКонца, ТекущаяДата());
    Запрос.УстановитьПараметр("КонечнаяДата", КонечнаяДата);
    
    Это делает ваш код более читаемым и легким для отладки.

Таким образом, выбор метода зависит от того, используете ли вы СКД или чистый запрос, а также от требований к производительности вашей системы. Для большинства задач в СКД лучшим решением будет использование выражений параметров, а для сложных запросов в коде — предварительный расчет даты средствами встроенного языка.

← На главную