К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как ограничить период в запросе 1С

Как ограничить период в запросе 1С: оператор ГДЕ с датами, параметры НачалоПериода и КонецПериода, виртуальные таблицы Обороты и Остатки, типичные ошибки.

Ограничение периода — одна из самых частых задач при написании запросов в 1С. Отчёты, обработки, движения документов почти всегда строятся за конкретный интервал: день, месяц, квартал. Если период не ограничить, запрос выберет все данные таблицы, что приведёт к долгому выполнению и неверным итогам.

В языке запросов 1С есть несколько способов задать период: через условие ГДЕ с параметрами даты, через параметры виртуальных таблиц регистров, через конструкцию ПЕРИОДАМИ и через функции работы с датами. У каждого способа свои особенности, границы включения дат и влияние на производительность.

Ниже разобраны практические приёмы: как передать даты в запрос, как правильно ограничить виртуальную таблицу, почему граница периода иногда «теряет» последний день и как проверить, что запрос вернул именно те данные, которые ожидались.

Основной способ: условие ГДЕ с параметрами даты

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

ВЫБРАТЬ

РеализацияТоваровУслуг.Ссылка,

РеализацияТоваровУслуг.Дата,

РеализацияТоваровУслуг.СуммаДокумента

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода

И РеализацияТоваровУслуг.Проведен

В коде модуля параметры устанавливаются так:

Запрос = Новый Запрос;

Запрос.Текст = ТекстЗапроса;

Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ДатаНачала));

Запрос.УстановитьПараметр("КонецПериода", КонецДня(ДатаОкончания));

Результат = Запрос.Выполнить().Выгрузить();

Ключевой момент — функция КонецДня(). Дата в 1С хранится с точностью до секунды, и если передать просто дату «31.03.0000 00:00:00», то все документы, созданные в течение последнего дня после полуночи, в выборку не попадут. Функция КонецДня() возвращает время 23:59:59, что закрывает весь день.

Ограничение периода в виртуальных таблицах регистров

Виртуальные таблицы регистров накопления и сведений — Остатки, Обороты, ОстаткиИОбороты — принимают период как параметры в круглых скобках. Это не только удобнее, но и быстрее: платформа строит выборку сразу по нужному интервалу, а не фильтрует полный набор записей.

ВЫБРАТЬ

ПродажиОбороты.Номенклатура,

ПродажиОбороты.КоличествоОборот,

ПродажиОбороты.СуммаОборот

ИЗ

РегистрНакопления.Продажи.Обороты(

&НачалоПериода,

&КонецПериода,

Месяц,

) КАК ПродажиОбороты

Первые два параметра — начало и конец периода, третий — периодичность группировки (Месяц, День, Регистратор), четвёртый — дополнительное условие отбора в виде текста. Для таблицы Остатки период задаётся одним параметром — моментом, на который считаются остатки.

Важное отличие: границы виртуальных таблиц работают по принципу «включая начало, исключая конец» для некоторых случаев при использовании момента времени. На практике надёжнее передавать в конец периода значение КонецДня(ДатаОкончания) либо объект Граница, если нужен точный контроль включения документа на границе.

Функции НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА в тексте запроса

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

ГДЕ

Продажи.Период МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, МЕСЯЦ)

И КОНЕЦПЕРИОДА(&Дата, МЕСЯЦ)

Допустимые значения второго аргумента: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД, а также варианты десятидневки и полугодия. Функция КОНЕЦПЕРИОДА возвращает последнюю секунду указанного интервала, поэтому проблема «потерянного последнего дня» здесь не возникает.

Эти же функции применяют для группировки данных по периодам: например, НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК Месяц позволяет свернуть движения по месяцам и построить динамику.

bsl
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ДатаОкончания));
// Вариант с границей, включающей документ на момент времени:
Запрос.УстановитьПараметр("ГраницаКонец", Новый Граница(МоментВремени, ВидГраницы.Включая));

Границы и момент времени: когда нужен объект Граница

При работе с остатками на конкретный документ или при проведении по регистрам важно, попадёт ли сам пограничный документ в выборку. Для этого в 1С существует объект Граница с двумя видами: Включая и Исключая.

Граница строится на основе момента времени (даты, ссылки на документ или объекта МоментВремени) и передаётся в параметр виртуальной таблицы. Например, остатки на начало документа, исключая сам документ:

ГраницаНачала = Новый Граница(ДокументОбъект.МоментВремени(), ВидГраницы.Исключая);

Запрос.УстановитьПараметр("Момент", ГраницаНачала);

Типичный сценарий — контроль остатков при проведении реализации: нужно получить остатки на момент документа без учёта его собственных движений. Если передать просто дату документа, а не границу, результат может включать или не включать пограничные записи непредсказуемо при совпадении времени до секунды.

Маршрут решения

Как выбрать способ ограничения

Нужны документы или записи за интервалУсловие ГДЕ с МЕЖДУ &Начало И &Конец, где конец — КонецДня()
Нужны обороты или остатки регистраПараметры виртуальной таблицы Обороты/Остатки — это быстрее
Известна одна дата, а нужен целый месяц или кварталНАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА прямо в тексте запроса
Важно положение относительно конкретного документаОбъект Граница с видом Включая или Исключая

Ограничение периода в СКД и динамических списках

В отчётах на системе компоновки данных период обычно не жёстко зашивают в запрос, а выносят в параметры СКД. В тексте запроса набор данных пишется с параметрами &НачалоПериода и &КонецПериода, а на закладке «Параметры» этим параметрам назначается тип Дата и, при необходимости, выражение для значения по умолчанию, например НачалоМесяца текущей даты.

Для виртуальных таблиц внутри СКД параметры периода лучше оставлять в скобках виртуальной таблицы, а не выносить в ГДЕ — иначе компоновщик может применить отбор уже после построения полной выборки, что резко увеличит время формирования отчёта на больших базах.

В динамических списках форм период ограничивается через свойство «Произвольный запрос» с теми же параметрами либо через стандартный отбор по полю Дата. Если список должен показывать только текущий месяц, параметры устанавливаются в обработчике ПриСозданииНаСервере формы.

В отчётах СКД удобно давать пользователю стандартный период: свяжите параметры НачалоПериода и КонецПериода с одним параметром типа СтандартныйПериод через выражения &Период.ДатаНачала и &Период.ДатаОкончания. Тогда пользователь выбирает «Этот месяц» одним кликом, а дата окончания автоматически содержит конец дня.
ПЗ
Практическое замечание

Типичные ошибки и проверка результата

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

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

Типичные ошибки
01
Передача даты окончания без КонецДня() — теряются все записи последнего дня после 00:00:00
02
Условие ГДЕ по полю Период вместо параметров виртуальной таблицы — запрос читает все записи регистра и работает медленно
03
Сравнение даты с параметром типа Строка — условие не срабатывает, выборка пустая или полная
04
Использование РАЗНОСТЬДАТ или преобразований над полем периода в условии — отключается индекс, резко растёт время выполнения
05
Забытый отбор по Проведен или Активность — в период попадают непроведённые и неактивные записи
Контрольные точки

Как проверить результат

Документ на первый день периода
Норма

Присутствует в выборке со временем 00:00:00 и позже

Тревожный признак

Отсутствует — проверьте НачалоДня()

Документ на последний день периода
Норма

Присутствует при любом времени в течение дня

Тревожный признак

Отсутствует — конец периода задан без КонецДня()

Итог по оборотам регистра
Норма

Совпадает с типовым отчётом за тот же период

Тревожный признак

Меньше — проверьте границы и отборы

Время выполнения на рабочей базе
Норма

Сопоставимо с типовыми отчётами

Тревожный признак

Запрос выполняется заметно дольше — период задан через ГДЕ по виртуальной таблице

Ограничения и производительность

Ограничение периода — главный инструмент ускорения запросов к большим таблицам, но способ задания влияет на план выполнения. Условия вида Период МЕЖДУ &Н И &К эффективно используют индекс по полю Период, которое входит в индексы регистров по умолчанию. А вот обёртывание поля в функции (например, НАЧАЛОПЕРИОДА(Период, ДЕНЬ) = &Дата) лишает оптимизатор возможности применить индекс.

Для регистров накопления с включёнными итогами виртуальные таблицы дополнительно выигрывают за счёт хранимых итогов по месяцам. Поэтому запрос оборотов за год через виртуальную таблицу выполнится быстрее, чем группировка физической таблицы движений с условием ГДЕ.

Если данных очень много, дополнительно ограничивайте выборку по измерениям (организация, склад, контрагент) прямо в параметрах виртуальной таблицы — четвёртый параметр-условие работает до выборки, а не после неё.

Итог

Что важно запомнить

Период ограничивают параметрами &НачалоПериода и &КонецПериода с обязательным КонецДня() для правой границы. Для регистров используйте параметры виртуальных таблиц — это быстрее. Для точной границы по документу применяйте объект Граница. Результат всегда проверяйте на документах первого и последнего дня интервала.

Частые вопросы

Почему в выборку не попадает последний день периода?

Потому что параметр конца периода содержит дату с нулевым временем (00:00:00), а документы за этот день имеют время позже. Передайте в параметр КонецДня(ДатаОкончания) или используйте в запросе функцию КОНЕЦПЕРИОДА(&Дата, ДЕНЬ).

Что быстрее: ГДЕ по полю Период или параметры виртуальной таблицы?

Параметры виртуальной таблицы. Они позволяют платформе сразу построить выборку по нужному интервалу и задействовать хранимые итоги. Условие ГДЕ по периоду применяется уже к готовому набору данных виртуальной таблицы и на больших объёмах работает заметно медленнее.

Как задать период «с начала года по выбранную дату»?

Передайте в запрос одну дату и вычислите границы в тексте: НАЧАЛОПЕРИОДА(&Дата, ГОД) для начала и КОНЕЦПЕРИОДА(&Дата, ДЕНЬ) для конца. Либо рассчитайте обе даты в коде через НачалоГода() и КонецДня() и передайте как два параметра.

Как получить остатки на момент документа без самого документа?

Создайте объект Граница: Новый Граница(Документ.МоментВремени(), ВидГраницы.Исключая) и передайте его в параметр виртуальной таблицы Остатки. Вид Исключая гарантирует, что собственные движения документа на эту секунду не попадут в расчёт.

Можно ли в СКД дать пользователю выбор периода «Месяц», «Квартал»?

Да. Добавьте параметр типа СтандартныйПериод, а параметрам НачалоПериода и КонецПериода задайте выражения &Период.ДатаНачала и &Период.ДатаОкончания. Стандартный период сам содержит корректный конец дня, поэтому проблема потери последнего дня исключена.

Почему запрос с условием по периоду выполняется очень долго?

Частые причины: период задан через ГДЕ вместо параметров виртуальной таблицы, поле периода обёрнуто в функцию (индекс не используется), либо в выборку попадают миллионы записей из-за отсутствия отбора по измерениям. Проверьте текст запроса и перенесите ограничения в параметры виртуальной таблицы.