При разработке и отладке отчетов в 1С:Предприятии программисты часто используют консоль запросов для быстрой проверки работоспособности запросов к виртуальным таблицам регистров накопления. Однако при работе с параметрами периодов в виртуальной таблице ОстаткиИОбороты можно столкнуться со специфической и на первый взгляд непонятной ошибкой: «Ошибка в выражении Момент времени: , дата...».
В этой статье мы подробно разберем, почему возникает данная ошибка, как правильно передавать параметры начала и конца периода в виртуальные таблицы, а также рассмотрим критически важные правила оптимизации запросов для повышения производительности базы данных.
Для начала проанализируем природу типа данных МоментВремени в платформе 1С. Этот тип данных предназначен для точного позиционирования события на временной шкале и физически состоит из двух обязательных полей:
Сочетание даты и ссылки позволяет СУБД однозначно выстроить последовательность документов, даже если они были записаны в пределах одной и той же секунды.
Суть ошибки вида Ошибка в выражении Момент времени: , дата 09.01.2020 0:00:00 заключается в том, что платформа попыталась сформировать объект момента времени, в котором заполнена только дата, а ссылка на документ отсутствует (равна пустому значению или Неопределено). Обратите внимание на запятую в тексте ошибки перед словом «дата» — она как раз указывает на то, что первое поле (ссылка) осталось пустым.
Почему это происходит в консоли запросов? В некоторых версиях консолей (как на обычных, так и на управляемых формах) механизм сериализации параметров работает некорректно. Когда разработчик выбирает тип параметра «МоментВремени» и указывает только дату, консоль не может передать полноценный объект в СУБД. В результате платформа генерирует аварийное завершение запроса.
В подавляющем большинстве случаев для работы с виртуальной таблицей ОстаткиИОбороты использовать сложный тип данных МоментВремени в качестве параметров начала и конца периода не требуется. Достаточно использовать обычный тип данных Дата.
Рассмотрим особенности поведения виртуальной таблицы ОстаткиИОбороты:
Остатки (которая рассчитывает итоги строго на начало указанной секунды и не включает движения этой секунды), таблица ОстаткиИОбороты всегда рассчитывает данные включительно по указанные границы периодов.08.01.2020 23:59:59), платформа гарантированно включит в расчет все обороты, сделанные в эту последнюю секунду дня.Поэтому самым простым и надежным решением в консоли запросов будет:
&НачалоПериода и &КонецПериода в таблице параметров консоли.07.01.2020 0:00:00 и 08.01.2020 23:59:59.Если мы пишем запрос непосредственно в коде, то обязательно нужно использовать функцию приведения конечной даты к концу дня, иначе документы, проведенные в течение последнего дня периода, не попадут в выборку. Посмотрим на пример передачи параметров в коде:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| АктивыОстаткиИОбороты.Портфель КАК Портфель,
| АктивыОстаткиИОбороты.Инструмент КАК Инструмент,
| АктивыОстаткиИОбороты.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
| АктивыОстаткиИОбороты.КоличествоПриход КАК КоличествоПриход,
| АктивыОстаткиИОбороты.КоличествоРасход КАК КоличествоРасход,
| АктивыОстаткиИОбороты.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток
|ИЗ
| РегистрНакопления.Активы.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, , , ) КАК АктивыОстаткиИОбороты";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(Объект.ДатаНачала));
// Обязательно приводим к концу дня для захвата всех движений последней секунды
Запрос.УстановитьПараметр("КонецПериода", КонецДня(Объект.ДатаОкончания));
Обратим внимание на первоначальный запрос автора темы. Фильтрация данных по конкретному портфелю выполнялась в секции ГДЕ:
// Неоптимальный вариант запроса!
ВЫБРАТЬ
АктивыОстаткиИОбороты.Портфель,
АктивыОстаткиИОбороты.Инструмент,
АктивыОстаткиИОбороты.КоличествоКонечныйОстаток
ИЗ
РегистрНакопления.Активы.ОстаткиИОбороты(&НачалоПериода, &КонецПериода) КАК АктивыОстаткиИОбороты
ГДЕ
АктивыОстаткиИОбороты.Портфель = &Портфель
Это классическая ошибка, которая на больших базах данных приводит к критическому снижению производительности и зависанию системы. Проанализируем, почему так происходит:
ГДЕ СУБД сначала полностью рассчитывает промежуточные остатки и обороты по абсолютно всем портфелям, инструментам и записям за указанный период. На это тратятся колоссальные ресурсы процессора и оперативной памяти сервера.Правильное решение: перенести отбор по измерению непосредственно в параметры виртуальной таблицы. В этом случае СУБД обратится к физическим таблицам итогов с уже наложенным фильтром по индексу. Расчет будет производиться только для одного конкретного портфеля, что ускорит выполнение запроса в десятки или сотни раз.
Давайте посмотрим, как выглядит правильный и оптимизированный запрос:
// Оптимальный вариант запроса с отбором в параметрах виртуальной таблицы
ВЫБРАТЬ
АктивыОстаткиИОбороты.Портфель КАК Портфель,
АктивыОстаткиИОбороты.Инструмент КАК Инструмент,
АктивыОстаткиИОбороты.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
АктивыОстаткиИОбороты.КоличествоПриход КАК КоличествоПриход,
АктивыОстаткиИОбороты.КоличествоРасход КАК КоличествоРасход,
АктивыОстаткиИОбороты.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток
ИЗ
РегистрНакопления.Активы.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода, , ,
Портфель = &Портфель
) КАК АктивыОстаткиИОбороты
Обратите внимание на синтаксис параметров виртуальной таблицы ОстаткиИОбороты. Отбор передается в качестве пятого параметра конструкции: ОстаткиИОбороты(НачалоПериода, КонецПериода, Периодичность, МетодДополнения, УсловиеОтбора).
В процессе обсуждения на форуме коллеги совершенно справедливо замечают, что при разработке реальных отчетов лучше сразу использовать Систему компоновки данных (СКД), а не писать жесткие запросы вручную.
СКД обладает мощным встроенным функционалом автоматического управления границами периодов:
&НачалоПериода и &КонецПериода со встроенным типом «СтандартныйПериод» на форме отчета, СКД самостоятельно выполнит все преобразования.Граница(КонецДня(Дата), ВидГраницы.Включая). Это гарантирует точное попадание всех документов на границе периода в итоговый результат без написания дополнительного программного кода.