При разработке сложных отчетов или обработок на платформе 1С:Предприятие программисты часто сталкиваются с необходимостью сделать запрос максимально динамическим. Типичная ситуация: мы сначала вычисляем некую дату (например, дату первого документа в цепочке или минимальную дату оплаты) и помещаем ее во временную таблицу. Казалось бы, логично далее использовать эту дату в качестве параметра НачалоПериода для виртуальной таблицы остатков или оборотов. Однако при попытке вставить подзапрос в круглые скобки виртуальной таблицы конструктор запроса выдает ошибку, а система отказывается выполнять такой код.
В этой статье мы подробно разберем, почему существует это ограничение, и рассмотрим три проверенных способа решения данной задачи, которые позволят вам эффективно работать с динамическими периодами.
Прежде чем переходить к решениям, проанализируем причину возникновения проблемы. В языке запросов 1С параметры виртуальных таблиц (такие как Остатки, Обороты, ОстаткиИОбороты) должны быть определены на момент подготовки плана выполнения запроса. Платформа 1С проектировалась таким образом, что параметры внутри круглых скобок () транслируются в SQL-запрос как константы или переменные сервера приложений.
Использование конструкции (ВЫБРАТЬ МИНИМУМ(Дата) ИЗ ВТ) внутри параметров виртуальной таблицы нарушает этот принцип. Система не может заранее знать, какое значение вернет подзапрос, чтобы оптимально выбрать таблицы итогов (агрегаты), которые будут задействованы. Таким образом, это жесткое ограничение платформы, которое невозможно обойти простым изменением синтаксиса внутри одного текста запроса.
Этот способ считается "золотым стандартом" при разработке в коде модулей. Разберем ситуацию по шагам. Если нам нужно использовать временную таблицу из первого запроса во втором, сохранив при этом данные, нам понадобится объект МенеджерВременныхТаблиц.
Рассмотрим алгоритм действий:
МенеджерВременныхТаблиц.Посмотрим на пример кода:
// 1. Инициализация менеджера и первого запроса
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.Текст =
"ВЫБРАТЬ
| МИНИМУМ(Документы.Дата) КАК МинДата
|ПОМЕСТИТЬ ВТ_ГраницаПериода
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Документы
|ГДЕ
| Документы.Ссылка = &Ссылка";
Запрос1.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
Запрос1.Выполнить();
// 2. Извлекаем значение даты из ВТ
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст = "ВЫБРАТЬ МинДата ИЗ ВТ_ГраницаПериода";
Результат = Запрос2.Выполнить().Выгрузить();
Если Результат.Количество() > 0 Тогда
НужнаяДата = Результат[0].МинДата;
Иначе
НужнаяДата = '00010101';
КонецЕсли;
// 3. Выполняем основной запрос с передачей параметра
Запрос3 = Новый Запрос;
Запрос3.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос3.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.СуммаОстаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(&Период, Счет = &Счет, , ) КАК ХозрасчетныйОстатки";
Запрос3.УстановитьПараметр("Период", НужнаяДата);
Запрос3.УстановитьПараметр("Счет", ПланСчетов.Хозрасчетный.ТоварыНаСкладах);
РезультатЗапроса = Запрос3.Выполнить();
Этот метод гарантирует максимальную производительность, так как СУБД получает четкое значение периода и сразу обращается к нужной таблице итогов.
Если задача решается в рамках построения отчета, то Система компоновки данных предоставляет более элегантный механизм — Связи наборов данных. В СКД можно создать два отдельных набора данных "Запрос".
Проанализируем этот механизм:
&НачалоПериода) указывается стандартное имя параметра.Разберем настройку связи по шагам:
Набор1, а приемником Набор2.НачалоПериода (или другой нужный).Набор1.В этом случае СКД при выполнении компоновки сама сначала вычислит значение из первого набора и "подставит" его в параметр второго набора на уровне генерации SQL-запроса. Для пользователя и разработчика это выглядит как автоматическая передача данных из одной выборки в параметры другой.
Иногда разработчики пытаются обойти ограничение, не указывая параметры в скобках вовсе, а накладывая фильтр позже. Рассмотрим, как это выглядит в тексте запроса:
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.СуммаОборот
ИЗ
РегистрНакопления.Продажи.Обороты(, &ДатаКон, , ) КАК Продажи
ГДЕ
Продажи.Период >= (ВЫБРАТЬ МИНИМУМ(ДатаНачала) ИЗ ВТ_События)
Внимание: проанализируем критические недостатки этого метода!
Когда вы оставляете поле Начало периода пустым, система 1С по умолчанию берет данные с самой ранней даты, доступной в регистре (0001-01-01). СУБД будет вынуждена прочитать и просуммировать все записи регистра с момента его создания до &ДатаКон. Секция ГДЕ применится уже после того, как виртуальная таблица будет сформирована в памяти. Если ваш регистр содержит миллионы записей за 10 лет, а вам нужен оборот за последние 2 дня, такой запрос может просто "повесить" сервер или выполняться крайне долго.
Этот метод допустим только для небольших справочных регистров или в исключительных случаях для регистров оборотов, если вы точно уверены, что объем данных невелик. Для регистров накопления с остатками или регистров бухгалтерии этот метод категорически не рекомендуется.
Выяснив причину ограничения и рассмотрев варианты решения, можно подвести итоги:
МенеджерВременныхТаблиц.ГДЕ без указания параметров в скобках виртуальной таблицы, чтобы не допустить деградации производительности системы.Применяя эти подходы, вы сможете создавать гибкие и производительные запросы, обходя синтаксические ограничения платформы 1С:Предприятие без потери качества кода.