Как задать период выборки в запросах 1С 8.3

· 9 мин

Содержание

Период в запросе ограничивают не в теле выборки, а там, где это понимает оптимизатор: простое сравнение поля Дата с двумя параметрами в условии ГДЕ или заданием границ прямо в параметрах виртуальной таблицы. Типичная ошибка — обернуть поле даты функцией (НачалоДня(Дата) = &Дата): запрос станет верным по результату, но перестанет попадать в индекс и начнёт перебирать всю таблицу.

Разберём рабочие конструкции: условие МЕЖДУ, параметры начала и конца периода, функции отсечения времени, границы периода в регистрах и то, как не потерять документы последнего дня из-за времени 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. 🔹 Границы периода в виртуальной таблице включаются — как и у МЕЖДУ.
  2. 🔹 Пустой первый параметр у Остатки() означает «на текущий момент» (последние данные).
  3. 🔹 Третий аргумент Обороты/ОстаткиИОбороты — периодичность (Период, День, Месяц и т. п.), от неё зависит детализация строк.
  4. 🔹 Дополнительный отбор (склад, номенклатура) кладут в последний параметр виртуальной таблицы как условие, а не в ГДЕ после — так отбор применяется до расчёта итогов.

Для регистра сведений с периодичностью используется СрезПоследних(&НаДату, Отбор) или СрезПервых — тоже с моментом времени в первом параметре. СрезПоследних(&НаДату, ) вернёт по каждому набору измерений последнюю запись не позже &НаДату. Если первый параметр не задать, срез берётся на текущий момент. Обычная ошибка — пытаться дополнительно ограничить СрезПоследних условием ГДЕ Период <= &НаДату: это бессмысленно, момент уже учтён в самом срезе.

Относительные периоды: текущий месяц, квартал, год

Чаще период отбирают не фиксированными датами, а относительно текущего момента: «с начала месяца», «этот квартал», «год по сегодня». Границы для этого считают в коде стандартными функциями дат, а в запрос передают уже готовыми параметрами.

Сейчас = ТекущаяДата();
Запрос.УстановитьПараметр("НачалоМесяца", НачалоМесяца(Сейчас));
Запрос.УстановитьПараметр("КонецДня",     КонецДня(Сейчас));
Запрос.УстановитьПараметр("НачалоГода",   НачалоГода(Сейчас));
Запрос.УстановитьПараметр("НачалоКвартала", НачалоКвартала(Сейчас));

Набор функций отсечения покрывает почти все относительные периоды:

  • 🗓 НачалоНедели / КонецНедели — неделя (начинается с понедельника);
  • 🗓 НачалоКвартала / КонецКвартала — квартал по дате;
  • 🗓 ДобавитьКДате(Дата, МЕСЯЦ, -1) — сдвиг на месяц назад для «прошлого периода»;
  • 🗓 РазностьДат(Дата1, Дата2, ДЕНЬ) — число дней между датами, если нужен расчёт длительности.

Для форм, где пользователь сам выбирает интервал, в платформе есть тип СтандартныйПериод и поле выбора периода. Объект сам отдаёт границы: Период.ДатаНачала и Период.ДатаОкончания — их и передают в параметры запроса. При этом ДатаОкончания у СтандартныйПериод уже содержит время 23:59:59, повторно оборачивать её в КонецДня не нужно.

Если относительный период нужен прямо внутри текста запроса (например, в отчёте без обработчика на встроенном языке), функции НачалоМесяца, НачалоГода и ДобавитьКДате доступны и там — применяйте их к параметру &ТекущаяДата, который передаёте снаружи, а не к полю таблицы.

Оптимизация выборки по периоду

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

  • 🚀 Не оборачивайте поле функцией. ГДЕ НАЧАЛОДНЯ(Продажи.Дата) = &Дата — поле под функцией, индекс не используется, идёт сканирование. Переносите функцию на параметр.
  • 🚀 Задавайте период в виртуальной таблице, а не отбирайте движения из основной таблицы регистра через ГДЕ. Разница на миллионах строк — секунды против минут.
  • 🚀 Избегайте ВЫБОР КОГДА по дате в условии отбора — сложное выражение мешает оптимизатору. Готовьте границы заранее в параметрах.
  • 🚀 Не тяните лишние поля. Чем уже выборка, тем меньше работы — особенно если дальше идёт соединение.
  • 🚀 Соединяйте уже отфильтрованные по периоду наборы. Если в запросе несколько таблиц, ограничьте период в каждой до соединения, а не после.

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

Проверка отбора по периоду
Выполнено: 0 / 5

Период в динамическом списке и отчёте

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

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

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

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

Граничные случаи и типичные ошибки

Несколько ситуаций, на которых отбор по периоду ломается тихо — без ошибки, но с неверным результатом.

Диагностика: выберите симптом

Ещё один нюанс — часовой пояс. Даты в 1С хранятся без привязки к зоне, поэтому ТекущаяДата() на сервере и локальное время пользователя могут различаться. Для границ периода, которые вводит пользователь, берите значение из формы, а не ТекущаяДата() на сервере, если сервер и пользователи в разных поясах.

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

Чем `МЕЖДУ` отличается от пары `>=` и `<=`?
Ничем по результату — МЕЖДУ &А И &Б эквивалентно Поле >= &А И Поле <= &Б, обе границы включаются. МЕЖДУ просто короче и читаемее.
Почему период в регистре задают в скобках, а не в ГДЕ?
Параметры виртуальной таблицы применяются до расчёта итогов — платформа сразу строит нужный срез. Условие в ГДЕ отрабатывает уже по готовому результату, поэтому отбор по периоду там и медленнее, и может посчитать итоги не за тот интервал.
Как ограничить выборку одним конкретным днём?
ГДЕ Таб.Дата МЕЖДУ НачалоДня(&Дата) И КонецДня(&Дата). Функции применяются к параметру — одно и то же значение превращается в границы 00:00:00 и 23:59:59.
Нужно ли отсекать время, если в документах оно всегда 00:00:00?
Даже если вы думаете, что время нулевое, у проведённых документов платформа часто проставляет время проведения. Безопаснее всегда пропускать верхнюю границу через КонецДня — лишним это не будет.
Можно ли сравнивать дату с текстовой константой периода?
Используйте литерал ДАТАВРЕМЯ(2026, 10, 1) или ДАТАВРЕМЯ(2026, 10, 1, 23, 59, 59) прямо в тексте запроса. Это надёжнее, чем передавать дату строкой и полагаться на преобразование.