Период в запросе 1С: параметры, виртуальные таблицы и СКД
Содержание
Период в запросе 1С задают двумя параметрами: в тексте пишут &НачалоПериода и &КонецПериода, а в коде передают значения через Запрос.УстановитьПараметр(). Для документов условие ставят в ГДЕ ... МЕЖДУ, для регистров — в параметры виртуальной таблицы Обороты(&НачалоПериода, &КонецПериода, ...). Конец периода всегда доводят до 23:59:59 функцией КонецДня(), иначе последний день выпадет из выборки.
Типичная ситуация: отчёт по продажам за март показывает сумму меньше, чем в оборотно-сальдовой ведомости. Разница ровно в документах за 31 марта. Причина почти всегда одна — в параметр конца периода ушла дата 31.03 0:00:00. Ниже — как установить период
в запросе правильно в каждом из типовых сценариев и где чаще всего ошибаются.
Базовый синтаксис: как задать период в тексте запроса
Запрос в 1С 8.3 не знает о «периоде» как отдельной сущности. Период — это просто две даты, которые вы сравниваете с полем Дата документа или Период регистра.
Минимальный рабочий пример для документов:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка,
| РеализацияТоваровУслуг.Дата,
| РеализацияТоваровУслуг.СуммаДокумента
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И РеализацияТоваровУслуг.Проведен";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(Дата1));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(Дата2));
Выборка = Запрос.Выполнить().Выбрать();
Что нужно знать про этот синтаксис:
- 📌
МЕЖДУвключает обе границы. Это то же самое, что>= &НачалоПериода И <= &КонецПериода. - 📌 Имя параметра произвольное.
&НачПер,&ДатаС,&Период1— работают одинаково. Главное, чтобы имя вУстановитьПараметр()совпало с текстом. - 📌 Если параметр не установлен, при выполнении запроса будет ошибка. Пустое значение 1С не подставляет.
- 📌 Регистр букв в названиях функций языка запросов не важен:
МЕЖДУиМеждуравнозначны.
Литерал даты прямо в тексте
Иногда нужна фиксированная дата, например для отладки в консоли запросов. Для этого есть функция ДАТАВРЕМЯ:
ГДЕ
Док.Дата МЕЖДУ ДАТАВРЕМЯ(2025, 1, 1) И ДАТАВРЕМЯ(2025, 3, 31, 23, 59, 59)
В рабочем коде так не делают: дату вшивают в текст только для разовых проверок. Пустая дата записывается как ДАТАВРЕМЯ(1, 1, 1) — так удобно отсекать незаполненные поля.
Функции языка запросов для работы с периодом
Внутри запроса доступны функции, которые двигают дату к границе периода или сдвигают её. Они избавляют от лишнего кода в модуле.
| Функция | Что делает | Пример |
|---|---|---|
НАЧАЛОПЕРИОДА(Дата, Вид) | Дата начала дня, недели, месяца, квартала, года | НАЧАЛОПЕРИОДА(&Дата, МЕСЯЦ) |
КОНЕЦПЕРИОДА(Дата, Вид) | Последняя секунда периода | КОНЕЦПЕРИОДА(&Дата, КВАРТАЛ) |
ДОБАВИТЬКДАТЕ(Дата, Вид, N) | Сдвиг на N единиц, N может быть отрицательным | ДОБАВИТЬКДАТЕ(&Дата, ДЕНЬ, -7) |
РАЗНОСТЬДАТ(Д1, Д2, Вид) | Разница между датами в единицах | РАЗНОСТЬДАТ(Док.Дата, &Дата, ДЕНЬ) |
ГОД, КВАРТАЛ, МЕСЯЦ, ДЕНЬ | Извлекают часть даты числом | МЕСЯЦ(Док.Дата) = 3 |
Вид периода пишется без кавычек: МИНУТА, ЧАС, ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД, а для НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА ещё и ДЕКАДА, ПОЛУГОДИЕ.
Пример: передаёте одну дату, а запрос сам строит границы месяца.
ГДЕ
Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, МЕСЯЦ) И КОНЕЦПЕРИОДА(&Дата, МЕСЯЦ)
Функции применяйте к параметру, а не к полю таблицы. НАЧАЛОПЕРИОДА(&Дата, МЕСЯЦ) вычисляется один раз. НАЧАЛОПЕРИОДА(Док.Дата, МЕСЯЦ) = &Месяц вычисляется для каждой строки и не даёт использовать индекс по дате.
Функции над полем уместны в списке ВЫБРАТЬ — например, чтобы сгруппировать документы по месяцам:
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Док.Дата, МЕСЯЦ) КАК Месяц,
СУММА(Док.СуммаДокумента) КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
ГДЕ
Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
НАЧАЛОПЕРИОДА(Док.Дата, МЕСЯЦ)
Относительный период: «последние 30 дней», «прошлый месяц»
В языке запросов 1С нет функции текущей даты. Точку отсчёта всегда передают параметром, а уже от неё строят относительный период.
Текущую дату на сервере берут функцией ТекущаяДатаСеанса(). Она учитывает часовой пояс сеанса, поэтому в типовых конфигурациях её предпочитают ТекущаяДата().
Вариант 1: границы считаются в коде
Сегодня = ТекущаяДатаСеанса();
// Прошлый месяц целиком
НачПрошлогоМесяца = НачалоМесяца(ДобавитьМесяц(Сегодня, -1));
КонПрошлогоМесяца = КонецМесяца(НачПрошлогоМесяца);
Запрос.УстановитьПараметр("НачалоПериода", НачПрошлогоМесяца);
Запрос.УстановитьПараметр("КонецПериода", КонПрошлогоМесяца);
Последние 30 дней включая сегодня: начало — НачалоДня(Сегодня) - 29 * 86400, конец — КонецДня(Сегодня). Дата в 1С хранится с точностью до секунды, поэтому прибавление числа сдвигает её на секунды: 86 400 — это сутки.
Вариант 2: границы считает сам запрос
ГДЕ
Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ТекущаяДата, МЕСЯЦ, -1), МЕСЯЦ)
И КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(&ТекущаяДата, МЕСЯЦ, -1), МЕСЯЦ)
Передаёте один параметр ТекущаяДата. Удобно, когда текст запроса хранится в макете или в СКД и вы не хотите плодить параметры.
| Нужный период | Начало | Конец |
|---|---|---|
| Текущий месяц | НачалоМесяца(Сегодня) | КонецМесяца(Сегодня) |
| Прошлый квартал | НачалоКвартала(ДобавитьМесяц(Сегодня, -3)) | КонецКвартала(ДобавитьМесяц(Сегодня, -3)) |
| С начала года | НачалоГода(Сегодня) | КонецДня(Сегодня) |
| Последние 7 дней | НачалоДня(Сегодня) - 6 * 86400 | КонецДня(Сегодня) |
Период в параметрах виртуальных таблиц регистров
Для регистров накопления и бухгалтерии период задают не в ГДЕ, а в скобках после имени виртуальной таблицы. Это главное правило запросов к регистрам.
Обороты
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.Период,
Продажи.КоличествоОборот,
Продажи.СуммаОборот
ИЗ
РегистрНакопления.Продажи.Обороты(
&НачалоПериода,
&КонецПериода,
Месяц,
Организация = &Организация) КАК Продажи
Параметры по порядку: начало, конец, периодичность, условие. Периодичность (Запись, Регистратор, День, Неделя, Месяц, Квартал, Год, Период, Авто) определяет, с каким шагом разобьются обороты. Значение Период означает одну строку на весь интервал — поле Период в выборке тогда недоступно.
Остатки и граница момента времени
Таблица Остатки принимает не интервал, а одну точку: Остатки(&МоментВремени, Условие). И здесь главная ловушка.
Остатки на дату 31.03.2025 0:00:00 — это остатки на начало 31 марта, без движений этого дня.
Если нужен остаток на конец дня, передайте КонецДня(Дата) или, надёжнее, границу с включением:
Граница = Новый Граница(КонецДня(Дата), ВидГраницы.Включая);
Запрос.УстановитьПараметр("МоментВремени", Граница);
Внутри документа остатки «на момент проведения» получают так: Новый Граница(Ссылка.МоментВремени(), ВидГраницы.Исключая). Тогда в остатки не попадут движения самого документа, и он не будет учитывать сам себя.
ОстаткиИОбороты
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
День,
ДвиженияИГраницыПериода,
Склад = &Склад)
Четвёртый параметр — метод дополнения. Движения выводит только дни, в которых были движения. ДвиженияИГраницыПериода добавляет строки на начало и конец интервала, даже если движений там не было: так начальный остаток периода не теряется.
Срез последних в регистре сведений
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ВидЦены)
Здесь тоже одна точка. Срез на 0:00:00 не увидит цену, установленную в тот же день в 10:00. Передавайте КонецДня(Дата), если нужна цена на конец дня.
Таблица соответствий
| Таблица | Что передаётся | Где ошибаются |
|---|---|---|
Обороты | начало и конец | конец без КонецДня |
Остатки | одна точка | дата без времени — остаток на начало дня |
ОстаткиИОбороты | начало, конец, периодичность, метод | нет строк за дни без движений |
СрезПоследних / СрезПервых | одна точка | дата без времени |
Период из формы и из схемы компоновки данных
В пользовательском интерфейсе период обычно выбирают одним полем типа СтандартныйПериод. У него есть свойства ДатаНачала, ДатаОкончания и Вариант (например, ВариантСтандартногоПериода.ЭтотМесяц).
Форма обработки или отчёта на запросе
- Добавьте реквизит формы с типом
СтандартныйПериод, напримерПериод. - Перетащите его на форму — платформа покажет поле с кнопкой выбора периода.
- В серверной процедуре передайте даты в запрос:
&НаСервере
Процедура СформироватьНаСервере()
Запрос = Новый Запрос(ТекстЗапроса());
Запрос.УстановитьПараметр("НачалоПериода", Период.ДатаНачала);
Запрос.УстановитьПараметр("КонецПериода", КонецДня(Период.ДатаОкончания));
Результат = Запрос.Выполнить();
// вывод результата
КонецПроцедуры
ДатаОкончания у стандартного периода, выбранного в диалоге, уже содержит 23:59:59. Но если пользователь не выбирал период, дата будет пустой. КонецДня() ничего не портит, поэтому вызывать его лучше всегда.
Пустой СтандартныйПериод даёт обе даты 01.01.0001. Запрос с такими параметрами вернёт пустую выборку без ошибки. Проверяйте ЗначениеЗаполнено(Период.ДатаОкончания) и подставляйте конец текущего дня, если пользователь ничего не указал.
Схема компоновки данных
В СКД периодом управляют параметры компоновки. Если в тексте набора данных есть &НачалоПериода и &КонецПериода, конструктор создаст их на закладке «Параметры» сам.
Практичная схема, которую используют в типовых отчётах:
- На закладке «Параметры» добавьте параметр
Периодс типомСтандартный период. - Параметрам
НачалоПериодаиКонецПериодапропишите в колонке «Выражение»&Период.ДатаНачалаи&Период.ДатаОкончания. - Снимите им флажок доступности пользователю — пользователь видит только одно поле «Период».
- Для
Периодвключите «Включать в пользовательские настройки», чтобы поле вышло в шапку отчёта.
Для дат в СКД доступны типы стандартной даты начала: значение по умолчанию «Начало этого месяца», «Начало этого года» и подобные. Это относительный период без единой строки кода.
В тексте запроса СКД параметры виртуальных таблиц можно обернуть в фигурные скобки:
РегистрНакопления.Продажи.Обороты({(&НачалоПериода)}, {(&КонецПериода)}, Авто, ) КАК Продажи
Фигурные скобки делают параметр необязательным: если значение не задано, компоновщик выбросит условие, а не упадёт с ошибкой.
Как ускорить запрос с периодом
Медленный отчёт за год почти всегда связан с тем, где и как стоит условие на дату.
- ⚡ Условие в параметрах виртуальной таблицы, а не в ГДЕ. Параметры виртуальной таблицы отбирают данные до свёртки и могут использовать итоги регистра. Условие в
ГДЕсрабатывает уже после того, как таблица собрана за всё время. - ⚡ Без функций над полем даты.
ГОД(Док.Дата) = 2025— полный просмотр таблицы.Док.Дата МЕЖДУ &Нач И &Кон— поиск по индексу. - ⚡ Периодичность не мельче нужной.
ЗаписьилиРегистратордают по строке на каждое движение. Для отчёта по месяцам ставьтеМесяц. - ⚡ Узкий интервал. Отчёт «за всё время» по большому регистру — это сотни тысяч строк. Задайте период по умолчанию в форме или СКД.
- ⚡ Временные таблицы. Если один и тот же отбор по периоду нужен в нескольких частях пакета, сохраните его в
ПОМЕСТИТЬ ВТ_Документыи проиндексируйте по ключевому полю.
Сравнение двух вариантов одного запроса:
// Медленно: остатки за всё время, отбор потом
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Ост
ГДЕ Ост.Склад = &Склад
// Быстро: отбор внутри виртуальной таблицы
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&МоментВремени, Склад = &Склад) КАК Ост
Для «пустых» периодов, когда нужен месяц в выборке даже без продаж, используйте итоги с дополнением:
ИТОГИ СУММА(СуммаОборот) ПО
Период ПЕРИОДАМИ(МЕСЯЦ, &НачалоПериода, &КонецПериода)
Обходите такую выборку вызовом Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Период", "Все"): третий параметр "Все" выводит строки и для месяцев без данных.
Ошибки при установке периода и что с ними делать
Отдельно про часовые пояса. Если сервер и пользователи работают в разных поясах, ТекущаяДата() вернёт время сервера. Для периода «сегодня» используйте ТекущаяДатаСеанса(), иначе после полуночи по Москве у пользователя в Новосибирске отчёт может захватить не тот день.