Как задать период выборки в запросах 1С 8.3
Содержание
Период в запросе ограничивают не в теле выборки, а там, где это понимает оптимизатор: простое сравнение поля Дата с двумя параметрами в условии ГДЕ или заданием границ прямо в параметрах виртуальной таблицы. Типичная ошибка — обернуть поле даты функцией (НачалоДня(Дата) = &Дата): запрос станет верным по результату, но перестанет попадать в индекс и начнёт перебирать всю таблицу.
Разберём рабочие конструкции: условие МЕЖДУ, параметры начала и конца периода, функции отсечения времени, границы периода в регистрах и то, как не потерять документы последнего дня из-за времени 00:00:00.
Отбор по дате через условие ГДЕ
Базовая форма — сравнение реквизита Дата с двумя значениями. Самый читаемый вариант — оператор МЕЖДУ:
ВЫБРАТЬ
Продажи.Ссылка,
Продажи.Дата,
Продажи.СуммаДокумента
ИЗ
Документ.РеализацияТоваровУслуг КАК Продажи
ГДЕ
Продажи.Дата МЕЖДУ &НачалоПериода И &КонецПериода
Оператор МЕЖДУ включает обе границы. То есть МЕЖДУ &НачалоПериода И &КонецПериода — это то же самое, что Дата >= &НачалоПериода И Дата <= &КонецПериода. Это важно помнить при стыковке соседних интервалов, чтобы один документ не попал сразу в два отчёта.
Если нужна только нижняя или только верхняя граница, обходятся одним сравнением:
- 📅 «с начала года» —
ГДЕ Продажи.Дата >= &НачалоГода; - 📅 «по сегодняшний день» —
ГДЕ Продажи.Дата <= &КонецПериода; - 📅 «строго до даты» —
ГДЕ Продажи.Дата < &Граница(без равенства).
Поле Дата у документа — это момент времени: дата плюс время с точностью до секунды. Поэтому значение, которое вы кладёте в параметр, тоже должно быть моментом времени, а не «голой» датой. Про это — следующий раздел.
Параметры начала и конца периода
Значения &НачалоПериода и &КонецПериода задаются снаружи запроса — в коде, который его выполняет. Имя параметра в тексте пишется через &, а в коде передаётся методом УстановитьПараметр:
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ДатаКонца));
Результат = Запрос.Выполнить();
Почему именно НачалоДня и КонецДня, а не сами даты из формы? Когда пользователь выбирает в поле периода «01.10 — 31.10», платформа обычно подставляет время 00:00:00. Если передать 31.10.2026 00:00:00 как верхнюю границу, то все документы за 31 октября с временем позже полуночи в выборку не попадут — а это почти все документы дня.
Функции отсечения времени решают задачу:
| Функция | Что возвращает | Для чего |
|---|---|---|
НачалоДня(Дата) | дата, время 00:00:00 | нижняя граница периода |
КонецДня(Дата) | дата, время 23:59:59 | верхняя граница периода |
НачалоМесяца(Дата) | 1-е число, 00:00:00 | помесячные отчёты |
КонецМесяца(Дата) | последнее число, 23:59:59 | помесячные отчёты |
НачалоГода / КонецГода | границы года | годовые выборки |
Эти же функции есть и внутри языка запросов — их можно вызывать прямо в тексте, но только над параметрами или константами, не над полем таблицы:
ГДЕ
Продажи.Дата МЕЖДУ НачалоМесяца(&ОпорнаяДата) И КонецМесяца(&ОпорнаяДата)
Здесь функция применяется к параметру &ОпорнаяДата — её значение вычисляется один раз, индекс по полю Дата продолжает работать.
Время в границах: как не потерять документы
Самая частая претензия к отбору по дате — «последний день периода выпал». Причина почти всегда в верхней границе.
- ⛔
Дата <= &КонецПериода, где в параметре31.10.2026 00:00:00— потеряете весь день 31-го. - ✅
Дата <= КонецДня(&КонецПериода)— время станет23:59:59, день войдёт целиком.
КонецДня возвращает 23:59:59 — последнюю секунду суток. Документы с этим временем включаются. Если в базе есть записи с более поздним временем в пределах суток, их не бывает: время момента в 1С ограничено секундами, позже 23:59:59 в тех же сутках значения нет.
Второй приём для верхней границы — сдвинуть её на начало следующего дня и использовать строгое неравенство:
ГДЕ
Продажи.Дата >= НачалоДня(&Дата1)
И Продажи.Дата < НачалоДня(ДобавитьКДате(&Дата2, ДЕНЬ, 1))
ДобавитьКДате(&Дата2, ДЕНЬ, 1) прибавляет один день, НачалоДня отсекает время, строгий знак < исключает полночь следующих суток. Результат тот же, что и с КонецДня, но без «магической секунды» — некоторым этот вариант кажется честнее.
Не сравнивайте дату документа с пустой датой напрямую как с границей. Пустая дата ('00010101000000') меньше любой реальной, поэтому условие Дата >= &Пусто пропустит вообще всё, а не «ничего». Проверяйте незаполненность отдельно.
Период в виртуальных таблицах регистров
Для регистров накопления и сведений период задаётся не в ГДЕ, а в параметрах виртуальной таблицы — в круглых скобках после её имени. Это принципиально: платформа строит выборку сразу по нужному срезу, а не фильтрует весь регистр.
Остатки берут на один момент:
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&НаДату, ) КАК Остатки
Обороты — за интервал, обе границы в скобках:
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Обороты
ОстаткиИОбороты — начальный остаток, обороты и конечный остаток за период:
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(
&НачалоПериода, &КонецПериода, Период, , , ) КАК Данные
Несколько правил по этим параметрам:
- 🔹 Границы периода в виртуальной таблице включаются — как и у
МЕЖДУ. - 🔹 Пустой первый параметр у
Остатки()означает «на текущий момент» (последние данные). - 🔹 Третий аргумент
Обороты/ОстаткиИОбороты— периодичность (Период,День,Месяци т. п.), от неё зависит детализация строк. - 🔹 Дополнительный отбор (склад, номенклатура) кладут в последний параметр виртуальной таблицы как условие, а не в
ГДЕпосле — так отбор применяется до расчёта итогов.
Для регистра сведений с периодичностью используется СрезПоследних(&НаДату, Отбор) или СрезПервых — тоже с моментом времени в первом параметре. СрезПоследних(&НаДату, ) вернёт по каждому набору измерений последнюю запись не позже &НаДату. Если первый параметр не задать, срез берётся на текущий момент. Обычная ошибка — пытаться дополнительно ограничить СрезПоследних условием ГДЕ Период <= &НаДату: это бессмысленно, момент уже учтён в самом срезе.
Относительные периоды: текущий месяц, квартал, год
Чаще период отбирают не фиксированными датами, а относительно текущего момента: «с начала месяца», «этот квартал», «год по сегодня». Границы для этого считают в коде стандартными функциями дат, а в запрос передают уже готовыми параметрами.
Сейчас = ТекущаяДата();
Запрос.УстановитьПараметр("НачалоМесяца", НачалоМесяца(Сейчас));
Запрос.УстановитьПараметр("КонецДня", КонецДня(Сейчас));
Запрос.УстановитьПараметр("НачалоГода", НачалоГода(Сейчас));
Запрос.УстановитьПараметр("НачалоКвартала", НачалоКвартала(Сейчас));
Набор функций отсечения покрывает почти все относительные периоды:
- 🗓
НачалоНедели/КонецНедели— неделя (начинается с понедельника); - 🗓
НачалоКвартала/КонецКвартала— квартал по дате; - 🗓
ДобавитьКДате(Дата, МЕСЯЦ, -1)— сдвиг на месяц назад для «прошлого периода»; - 🗓
РазностьДат(Дата1, Дата2, ДЕНЬ)— число дней между датами, если нужен расчёт длительности.
Для форм, где пользователь сам выбирает интервал, в платформе есть тип СтандартныйПериод и поле выбора
периода. Объект сам отдаёт границы: Период.ДатаНачала и Период.ДатаОкончания — их и передают в параметры запроса. При этом ДатаОкончания у СтандартныйПериод уже содержит время 23:59:59, повторно оборачивать её в КонецДня не нужно.
Если относительный период нужен прямо внутри текста запроса (например, в отчёте без обработчика на встроенном языке), функции НачалоМесяца, НачалоГода и ДобавитьКДате доступны и там — применяйте их к параметру &ТекущаяДата, который передаёте снаружи, а не к полю таблицы.
Оптимизация выборки по периоду
Отбор по дате — одно из мест, где запрос легко «кладут» на большой базе. Несколько приёмов, которые реально ускоряют:
- 🚀 Не оборачивайте поле функцией.
ГДЕ НАЧАЛОДНЯ(Продажи.Дата) = &Дата— поле под функцией, индекс не используется, идёт сканирование. Переносите функцию на параметр. - 🚀 Задавайте период в виртуальной таблице, а не отбирайте движения из основной таблицы регистра через
ГДЕ. Разница на миллионах строк — секунды против минут. - 🚀 Избегайте
ВЫБОР КОГДАпо дате в условии отбора — сложное выражение мешает оптимизатору. Готовьте границы заранее в параметрах. - 🚀 Не тяните лишние поля. Чем уже выборка, тем меньше работы — особенно если дальше идёт соединение.
- 🚀 Соединяйте уже отфильтрованные по периоду наборы. Если в запросе несколько таблиц, ограничьте период в каждой до соединения, а не после.
Проверить, попал ли запрос в индекс, помогает технологический журнал или замер времени в консоли запросов на копии рабочей базы — на боевой тяжёлые запросы лучше не гонять в часы работы пользователей.
Период в динамическом списке и отчёте
Если запрос работает не в коде, а в динамическом списке формы или в отчёте на системе компоновки данных, передать параметры напрямую методом УстановитьПараметр не получится — границы задают через механизмы настройки.
В динамическом списке период чаще всего ограничивают отбором по полю Дата: отбор добавляют в настройках списка, а значение связывают с реквизитом формы, который пользователь меняет в шапке. Второй путь — параметр в тексте запроса списка, который заполняется в обработчике ПриСозданииНаСервере через свойство Параметры динамического списка.
В отчёте на СКД для периода обычно заводят параметры НачалоПериода и КонецПериода либо один параметр типа СтандартныйПериод. В настройках они выводятся пользователю как поле выбора периода, а в запросе компоновки используются так же, как &НачалоПериода и &КонецПериода в обычном запросе. Отдельно отсекать время не требуется — компоновщик подставляет границы с учётом времени.
Не дублируйте ограничение периода: если интервал уже задан параметром виртуальной таблицы, не добавляйте ещё и отбор по Период в настройках — результат может «схлопнуться» до пустого, когда два условия не совпадают по границам.
Граничные случаи и типичные ошибки
Несколько ситуаций, на которых отбор по периоду ломается тихо — без ошибки, но с неверным результатом.
КонецДня() или сравнивайте со строгим < от начала следующего дня.МЕЖДУ включает обе границы, поэтому дата, равная концу одного и началу другого интервала, считается дважды. Делайте верхнюю границу строгой: < НачалоДня(следующий период).ГДЕ из основной таблицы движений. Перенесите интервал в параметры виртуальной таблицы Обороты/ОстаткиИОбороты.Дата обёрнуто функцией (НачалоМесяца(Таб.Дата)). Переносите функцию на параметр, поле оставляйте «голым».Таб.Дата <> ДАТАВРЕМЯ(1,1,1) или проверяйте заполненность до запроса.Ещё один нюанс — часовой пояс. Даты в 1С хранятся без привязки к зоне, поэтому ТекущаяДата() на сервере и локальное время пользователя могут различаться. Для границ периода, которые вводит пользователь, берите значение из формы, а не ТекущаяДата() на сервере, если сервер и пользователи в разных поясах.
Частые вопросы
Чем `МЕЖДУ` отличается от пары `>=` и `<=`?
МЕЖДУ &А И &Б эквивалентно Поле >= &А И Поле <= &Б, обе границы включаются. МЕЖДУ просто короче и читаемее.Почему период в регистре задают в скобках, а не в ГДЕ?
ГДЕ отрабатывает уже по готовому результату, поэтому отбор по периоду там и медленнее, и может посчитать итоги не за тот интервал.Как ограничить выборку одним конкретным днём?
ГДЕ Таб.Дата МЕЖДУ НачалоДня(&Дата) И КонецДня(&Дата). Функции применяются к параметру — одно и то же значение превращается в границы 00:00:00 и 23:59:59.Нужно ли отсекать время, если в документах оно всегда 00:00:00?
КонецДня — лишним это не будет.Можно ли сравнивать дату с текстовой константой периода?
ДАТАВРЕМЯ(2026, 10, 1) или ДАТАВРЕМЯ(2026, 10, 1, 23, 59, 59) прямо в тексте запроса. Это надёжнее, чем передавать дату строкой и полагаться на преобразование.