урок 5 из 18

Параметры и даты

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

18 мин чтения · 4 главы ·4 задания ·домашняя работа

Параметр вместо вписанного значения

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

ВЫБРАТЬ
    Док.Номер КАК Номер,
    Док.СуммаДокумента КАК Сумма
ИЗ
    Документ.РеализацияТоваровУслуг КАК Док
ГДЕ
    Док.Проведён
    И Док.Дата >= &Начало
    И Док.Дата <= &Конец
    И Док.Склад = &Склад
текст запросаГДЕ Дата >= &Начало1 марта → отчёт за март1 июня → отчёт за июньодин разобранный запрос, разные значения — база не разбирает текст заново
Текст запроса остаётся одним и тем же, меняется только подставленное значение.

Параметр пишется знаком & и именем. Имя своё, любое понятное; регистр букв не важен, но принято писать так же, как называется то, что подставляется. Значение задаётся снаружи: в коде — Запрос.УстановитьПараметр("Начало", НачалоМесяца), в отчёте на компоновке — настройкой или самой компоновкой, в консоли запросов — в отдельной табличке под текстом.

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

Незаполненный параметр не ошибка, а частая причина странного результата. Если &Склад не установили вовсе, платформа подставит неопределённое значение, и сравнение с ним не даст ни одной строки. Отчёт при этом построится и покажет пустоту — а человек скажет «отчёт сломался». Поэтому в коде параметры устанавливают все и сразу, а в отчётах следят, чтобы у настройки было значение по умолчанию.

Параметр можно передать и списком — тогда он работает со словом В: Док.Склад В (&Склады). В коде туда кладут массив или список значений, в отчёте — множественный отбор. Один и тот же текст запроса при этом обслуживает и «один склад», и «пять складов», и «все».

А теперь важное ограничение, о которое спотыкаются все, кто пришёл из других языков: параметром подставляется значение, и только значение. Имя таблицы, имя поля, ключевое слово, знак сравнения — всё это часть текста запроса, и параметром их не заменить. Написать «ИЗ &Таблица» нельзя; написать «ГДЕ Поле &Знак &Значение» тоже нельзя.

Причина простая и та же, что у запрета на склейку: запрос разбирается один раз, до подстановки значений. Разобрать текст, в котором неизвестно даже, откуда брать строки, невозможно. Если таблица или условие действительно должны меняться, текст запроса собирают в коде — и вот там склейка законна, потому что склеиваются части текста, а не значения. Значения при этом всё равно остаются параметрами.

Дата со временем и конец периода

Теперь про даты — и начнём сразу с ошибки, которую делают все без исключения.

Дата в 1С всегда содержит время. Даже когда его не видно. Когда человек выбирает в отчёте «по 31 марта», платформа передаёт 31.03.2026 00:00:00 — полночь, то есть самое начало дня. И условие Док.Дата <= &Конец честно отсекает всё, что случилось позже полуночи: то есть весь рабочий день тридцать первого марта.

&Конец = 31.03то есть 31.03 00:00:00документ31.03 14:20не попалв отчёт31.03 14:20 больше, чем 31.03 00:00:00, — условие «меньше либо равно» его отсекаетлечится это КОНЕЦПЕРИОДА(&Конец, ДЕНЬ) — 31.03 23:59:59или условием «меньше, чем начало следующего дня»
Дата без времени — это полночь: конец периода отрезает почти сутки документов.

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

Лечится это одним из двух способов, и оба правильные.

Первый: дотянуть конец до конца дня.

ГДЕ
    Док.Дата МЕЖДУ &Начало И КОНЕЦПЕРИОДА(&Конец, ДЕНЬ)

Функция КОНЕЦПЕРИОДА превращает 31.03 00:00:00 в 31.03 23:59:59. Обратите внимание: функция применяется к параметру, а не к полю, — значит, вычисляется она один раз, и правило «слева от сравнения должно стоять поле» не нарушено.

Второй способ: взять строго меньше начала следующего дня.

ГДЕ
    Док.Дата >= &Начало
    И Док.Дата < ДОБАВИТЬКДАТЕ(&Конец, ДЕНЬ, 1)

Здесь граница не включается, и последняя секунда никого не волнует. Многие предпочитают именно этот вариант: 23:59:59 — это не совсем конец дня, ведь между 23:59:59 и полуночью помещается ещё 999 миллисекунд, и документ, записанный в эту щель, пропадёт. Случается такое редко, но в обмене и в автоматических документах — случается.

Что выбрать — вопрос вкуса и правил конкретной команды. Выбрать что-то одно и держаться — обязательно.

Из того же корня растёт вторая ошибка, поменьше: равенство с датой почти никогда не срабатывает. Условие Док.Дата = &Дата найдёт только те документы, что записаны ровно в переданную секунду, — то есть, скорее всего, ни одного. «За такой-то день» — это всегда диапазон:

ГДЕ
    Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ)
                И КОНЕЦПЕРИОДА(&Дата, ДЕНЬ)

Обе функции применены к параметру, и обе вычисляются один раз. Такой отбор база выполняет по индексу, а условие читается ровно так, как задумано: от первого момента дня до последнего.

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

Функции работы с датами

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

НАЧАЛОПЕРИОДА(дата, МЕСЯЦ)17.03 14:20 → 01.03 00:00:00КОНЕЦПЕРИОДА(дата, МЕСЯЦ)17.03 14:20 → 31.03 23:59:59ДОБАВИТЬКДАТЕ(дата, ДЕНЬ, 30)срок оплаты через 30 днейРАЗНОСТЬДАТ(а, б, ДЕНЬ)сколько дней междучетыре функции покрывают почти всю работу с датами в запросе
Начало и конец периода считаются от даты, а не от календаря в голове.

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

ДОБАВИТЬКДАТЕ(дата, часть, сколько) — сдвиг. Срок оплаты через тридцать дней, следующий месяц, предыдущий год. Число может быть отрицательным: ДОБАВИТЬКДАТЕ(&Начало, МЕСЯЦ, -1) — тот же период месяцем раньше, и это ровно то, что нужно для колонки «прошлый период» в отчёте.

РАЗНОСТЬДАТ(дата1, дата2, часть) — сколько частей между двумя датами. Так считают просрочку в днях, возраст задолженности, время между отгрузкой и оплатой. Порядок аргументов важен: из второй даты вычитается первая, и если получить нужно положительное число, раньшая дата пишется первой.

ДАТАВРЕМЯ(год, месяц, день) — дата, написанная прямо в запросе. Так пишут заведомо постоянные границы: ДАТАВРЕМЯ(2026, 1, 1). Можно добавить час, минуту и секунду шестью числами. Отдельный случай — ДАТАВРЕМЯ(1, 1, 1): это «нулевая» дата, то самое значение незаполненного реквизита даты, о котором шла речь в прошлом уроке.

И три функции, выдёргивающие часть даты: ГОД(дата), МЕСЯЦ(дата), ДЕНЬ(дата). Ими удобно строить группировки по годам и месяцам — и именно их не стоит писать в условиях отбора, потому что функция по полю лишает базу индекса. Разница простая: в списке полей ГОД считает колонку, в ГДЕ — портит скорость.

С группировкой по месяцам связана ловушка, которая стоит отдельного абзаца. Казалось бы, естественно взять две колонки — ГОД(Дата) и МЕСЯЦ(Дата) — и группировать по ним. Работает это ровно до момента, когда понадобится сортировка или график: месяцы окажутся числами от 1 до 12, и январь следующего года встанет перед декабрём предыдущего. Правильный приём другой:

ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Док.Дата, МЕСЯЦ) КАК Месяц,
    СУММА(Док.СуммаДокумента) КАК Сумма

Колонка «Месяц» остаётся датой — первым числом месяца, — и сортируется как дата, и на графике ложится по оси времени правильно. Показать её человеку в виде «март 2026» — дело оформления отчёта, а не запроса. Тот же приём работает для дней, недель и кварталов.

Есть и функции поменьше, которые пригождаются реже: НЕДЕЛЯ(дата) — номер недели в году, ДЕНЬНЕДЕЛИ(дата) — номер дня недели от понедельника, ДЕНЬГОДА(дата) — номер дня с начала года. Ими строят отчёты вроде «продажи по дням недели» — и к ним относится ровно то же правило: в списке полей можно, в отборе не нужно.

Даты в параметрах виртуальных таблиц

У виртуальных таблиц регистров даты живут не в ГДЕ, а в параметрах самой таблицы, и это первое, что стоит помнить.

ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(
        &НаДату,
        Склад = &Склад) КАК Ост

Здесь &НаДату — это момент, на который считать остаток, и он тоже дата со временем. Остаток «на 31 марта» означает «на 31 марта в полночь», то есть до всех документов этого дня. Хотели «на конец марта» — пишите КОНЕЦПЕРИОДА(&Конец, ДЕНЬ), и это та же ошибка, что и в прошлой главе, только выглядит она иначе: не «не хватает последнего дня», а «остаток не сходится с документами».

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

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

ИЗ
    РегистрНакопления.Продажи.Обороты(
        &Начало, КОНЕЦПЕРИОДА(&Конец, ДЕНЬ),
        МЕСЯЦ, ) КАК Об

С периодичностью МЕСЯЦ у результата появляется колонка «Период» — и это опять дата, первое число месяца, ровно как в приёме из прошлой главы. Отсюда полезный вывод: помесячную динамику не нужно считать самому. Таблица оборотов умеет это сама, и считает она быстрее, чем группировка по документам, потому что читает уже свёрнутые итоги.

Периодичность РЕГИСТРАТОР раскладывает оборот по документам — так делают отчёты «из чего сложилась цифра». Это самая подробная и самая тяжёлая периодичность: строк получается столько же, сколько документов, и на годовом периоде такой отчёт лучше не открывать без отбора.

Отдельная тонкость — граница и момент времени. Когда нужен остаток «после всех движений такого-то документа», датой обойтись нельзя: в одну секунду может попасть несколько документов. Для этого в коде существует объект «Момент времени», а в запрос передаётся граница — значение, которое учитывает и дату, и порядок записи. Подробно это второй уровень; на первом достаточно знать, что такой случай существует и что «на дату» и «сразу после документа» — не одно и то же.

И последнее, практическое: где считать границы периода — в коде или в запросе? Обе привычки живы. Считать в коде и передавать готовые &Начало и &Конец — честнее: запрос тогда не знает ничего лишнего, а границы видны в отладчике. Считать в запросе функциями периода — короче, когда отчёт строится от одной даты. Плохо только третье — считать наполовину там и наполовину здесь: тогда при разборе «почему не сходится» приходится искать истину в двух местах сразу.

что нужнокак записать
Остаток на конец дняОстатки(КОНЕЦПЕРИОДА(&Конец, ДЕНЬ), …)
Обороты за месяцОбороты(&Начало, КОНЕЦПЕРИОДА(&Конец, ДЕНЬ), , )
Период месяцем раньшеДОБАВИТЬКДАТЕ(&Начало, МЕСЯЦ, -1)
Просрочка в дняхРАЗНОСТЬДАТ(Док.Дата, &Сегодня, ДЕНЬ)

Задания урока

Каждое проверяется сразу: ответ сверяется с эталоном, и на неверный вариант приходит разбор.

  1. Что верно про параметр запроса?средне
  2. Условие «Док.Дата <= &Конец» при выбранном «по 31 марта» включит документы тридцать первого числа.средне
  3. Соотнесите задачу и функцию.средне
  4. Остаток «на 31 марта» посчитается с учётом документов тридцать первого числа.сложно

Практика и материалы по теме курса

Пройти урок с проверкой заданий Курс «1С: запросы и отчёты» открыт и бесплатен, прогресс сохраняется.
Открыть курс