При разработке отчетов и запросов к регистрам накопления в 1С программисты часто сталкиваются с вопросом: как правильно передать границы периода в виртуальные таблицы, такие как Обороты? Нужно ли явно прописывать функции приведения даты к началу или концу дня непосредственно в тексте запроса, или платформа сделает это автоматически? В этой статье мы подробно разберем механизмы работы виртуальных таблиц, проанализируем влияние различных подходов на производительность и выясним, как гарантировать точность получаемых данных.
Разберем базовую ситуацию. Если мы передаем в параметр запроса &ДатаКонца значение типа "Дата", которое содержит только число (например, 01.01.2023), то фактически это значение интерпретируется как 01.01.2023 00:00:00. Рассмотрим, что произойдет при обращении к виртуальной таблице оборотов:
ВЫБРАТЬ
ПродажиОбороты.СтоимостьОборот
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНачала, &ДатаКонца) КАК ПродажиОбороты
Если в &ДатаКонца передано начало дня, то все движения, совершенные в течение этого дня (например, продажа в 14:30), не попадут в результат. Платформа воспримет это как требование получить данные строго до первой секунды указанной даты. Чтобы включить в оборот весь день целиком, нам необходимо передать в запрос время 23:59:59. Рассмотрим подробнее, какими способами это можно сделать.
Первый и самый очевидный способ — использование встроенных функций языка запросов НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА непосредственно в параметрах виртуальной таблицы. Проанализируем пример:
ВЫБРАТЬ
ПродажиОбороты.СтоимостьОборот
ИЗ
РегистрНакопления.Продажи.Обороты(
НАЧАЛОПЕРИОДА(&Дата1, ДЕНЬ),
КОНЕЦПЕРИОДА(&Дата2, ДЕНЬ)
) КАК ПродажиОбороты
Этот подход гарантирует, что вне зависимости от того, какое время пришло в параметрах &Дата1 и &Дата2, запрос всегда возьмет полный интервал. Однако у этого метода есть существенный недостаток, связанный с оптимизацией. Вычисление функций над параметрами внутри запроса может помешать СУБД эффективно построить план выполнения. СУБД не всегда может заранее определить, что результат функции — это константа, и это может привести к избыточным нагрузкам при сложных соединениях.
Наиболее правильным с точки зрения производительности и надежности считается подготовка параметров во встроенном языке 1С перед выполнением запроса. Рассмотрим, как это выглядит в коде:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПродажиОбороты.СтоимостьОборот
|ИЗ
| РегистрНакопления.Продажи.Обороты(&ДатаНачала, &ДатаКонца) КАК ПродажиОбороты";
// Приводим даты к началу и концу дня программно
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаКонца", КонецДня(ДатаКонца));
Почему это лучше? Выясним причину: когда мы передаем в запрос уже готовое значение, платформа транслирует его в SQL как простой литерал (конкретное значение даты). СУБД видит четкие границы периода и может максимально эффективно использовать индексы таблиц итогов и движений. Это исключает "сканирование" лишних данных и ускоряет выполнение отчета.
Иногда возникает вопрос: нужно ли использовать объект Граница для оборотных регистров, как мы это делаем для остатков? Напомним, что для получения остатков на конкретный момент времени с учетом движений самого документа используется конструкция:
Граница(КонецДня(Дата), ВидГраницы.Включая)
Однако для виртуальной таблицы Обороты использование объекта Граница чаще всего является избыточным. Таблица оборотов по своей сути работает с временным интервалом. Если вы передадите в качестве конца периода 23:59:59, платформа автоматически включит все записи, время которых меньше или равно этому значению. Использование Граница допустимо, но не дает никаких преимуществ перед простым использованием КонецДня в случае оборотов.
Разберем ситуацию, когда отчет строится на СКД. В СКД часто используется стандартный период. Если в параметрах виртуальной таблицы указаны стандартные имена &НачалоПериода и &КонецПериода, система при включенном автозаполнении умеет самостоятельно обрабатывать границы.
Посмотрим на важный нюанс: если пользователь выбирает период "Январь", СКД автоматически подставит 01.01.2023 00:00:00 и 31.01.2023 23:59:59. Но если вы вручную пропишете в тексте запроса КОНЕЦПЕРИОДА(&ДатаКонца, ДЕНЬ), вы можете переопределить поведение системы, что иногда приводит к неожиданным результатам (например, если пользователь намеренно указал время в параметре, а запрос его "сбросил" на конец дня).
Поэтому в СКД рекомендуется:
Проанализируем ситуацию с точки зрения СУБД. Существует понятие SARGable (Search Argumentable) запросов. Это запросы, которые позволяют эффективно использовать индексы. Когда вы пишете ГДЕ КОНЕЦПЕРИОДА(ДатаДокумента, ДЕНЬ) = &Параметр, вы делаете запрос "не-SARGable", так как СУБД должна вычислить функцию для каждой строки таблицы перед сравнением.
Хотя в параметрах виртуальной таблицы 1С старается оптимизировать такие вызовы, правило хорошего тона для разработчика 1С остается прежним: все, что можно вычислить вне запроса, должно быть вычислено вне запроса. Это касается и функций НАЧАЛОПЕРИОДА, КОНЕЦПЕРИОДА, ДОБАВИТЬКДАТЕ и других.
Разберем краткий алгоритм действий при работе с периодами оборотов:
НачалоДня() и КонецДня() в коде 1С при установке параметров запроса.Регистр.Обороты(&Начало, &Конец) вместо Регистр.Обороты(НАЧАЛОПЕРИОДА(...), ...).Следуя этим правилам, вы обеспечите не только точность бизнес-отчетов, но и высокую скорость их работы даже на больших объемах данных.