При разработке отчетов и обработок в среде «1С:Предприятие 8» начинающие разработчики часто сталкиваются с исключением во время выполнения метода Выполнить() объекта Запрос: «Не задано значение параметра». Выясним причину этой ошибки, рассмотрим типовые заблуждения при работе с параметрами запросов и разберем по шагам, как правильно передавать параметры, работать с иерархией подразделений и оптимизировать код.
В тексте запроса символ амперсанда (&) обозначает параметр, значение которого платформа ожидает получить до момента исполнения SQL-запроса на сервере базы данных. Если в секциях ГДЕ, ВЫБОР или в параметрах виртуальных таблиц объявлен параметр, например &ТекущееПодразделение, платформа 1С требует явного присвоения ему значения с помощью метода УстановитьПараметр().
Если разработчик забывает инициализировать хотя бы один параметр или допускает опечатку в его имени, метод Запрос.Выполнить() аварийно прерывает выполнение программы с ошибкой времени исполнения.
Проанализируем распространенные заблуждения, возникающие при инициализации параметров:
ВЫБРАТЬ с помощью псевдонима КАК ТекущееПодразделение формирует колонку в результирующей таблице, но никак не заполняет условие фильтрации &ТекущееПодразделение в секции ГДЕ. Параметр фильтрации всегда должен заполняться из внешней переменной.Запрос1.УстановитьПараметр("ТекущееПодразделение", ТекущееПодразделение) компилятор сообщает: Переменная не определена (ТекущееПодразделение), если переменная с таким именем не была объявлена или не является реквизитом формы/отчета.Справочники.ПодразделенияОрганизаций возвращает объект СправочникМенеджер, а не конкретный элемент данных. В параметр фильтра необходимо передавать значение типа СправочникСсылка или массив ссылок.Разберем по шагам, как корректно передать подразделение и другие параметры в объект Запрос перед его выполнением. Все параметры должны быть определены строго до вызова метода Выполнить().
Посмотрим на пример правильного заполнения параметров:
// 1. Получаем ссылку на подразделение из реквизита отчета или формы
ПодразделениеОтбора = Отчет.Подразделение; // СправочникСсылка.ПодразделенияОрганизаций
// 2. Создаем и настраиваем запрос
Запрос1 = Новый Запрос;
Запрос1.Текст =
"ВЫБРАТЬ
| ТекущиеКадровыеДанные.ТекущееПодразделение КАК Подразделение,
| ТекущиеКадровыеДанные.Сотрудник КАК Сотрудник
|ИЗ
| РегистрСведений.ТекущиеКадровыеДанныеСотрудников КАК ТекущиеКадровыеДанные
|ГДЕ
| ТекущиеКадровыеДанные.ТекущееПодразделение В ИЕРАРХИИ(&ТекущееПодразделение)
| И ТекущиеКадровыеДанные.ТекущееСостояние <> &Состояние";
// 3. Устанавливаем ВСЕ параметры, объявленные в тексте запроса
Запрос1.УстановитьПараметр("ТекущееПодразделение", ПодразделениеОтбора);
Запрос1.УстановитьПараметр("Состояние", Перечисления.СостоянияСотрудника.Увольнение);
// 4. Выполняем запрос
РезультатЗапроса = Запрос1.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Чтобы исключить падение системы из-за забытых параметров, рассмотрим полезный встроенный инструмент платформы. У объекта Запрос доступен метод НайтиПараметры(). Он возвращает коллекцию описаний всех параметров, встретившихся в тексте запроса. С его помощью можно программно проверить, все ли параметры были заданы разработчиком:
НайденныеПараметры = Запрос1.НайтиПараметры();
Для Каждого ОписаниеПараметра Из НайденныеПараметры Цикл
Если Не Запрос1.Параметры.Свойство(ОписаниеПараметра.Имя) Тогда
ВызватьИсключение "Не заполнен обязательный параметр запроса: " + ОписаниеПараметра.Имя;
КонецЕсли;
КонецЦикла;
В исходных текстах начинающих разработчиков часто встречается проверка иерархии через обращение к родителям:
// Антипаттерн: избыточные левые соединения и жесткое ограничение уровней
ГДЕ
ТекущиеКадровыеДанные.ТекущееПодразделение = &ТекущееПодразделение
ИЛИ ТекущиеКадровыеДанные.ТекущееПодразделение.Родитель = &ТекущееПодразделение
ИЛИ ТекущиеКадровыеДанные.ТекущееПодразделение.Родитель.Родитель = &ТекущееПодразделение
Каждое обращение через точку к реквизиту Родитель порождает неявное соединение (LEFT JOIN) в итоговом SQL-запросе, что существенно снижает скорость работы отчета и не позволяет учитывать подразделения с большей вложенностью.
Для проверки принадлежности элемента ветке иерархии любой глубины используем специальную конструкцию 1С: В ИЕРАРХИИ:
ГДЕ
ТекущиеКадровыеДанные.ТекущееПодразделение В ИЕРАРХИИ (&ТекущееПодразделение)
Если же отчет строится без фильтрации по конкретному подразделению (по всей организации), условие отбора следует полностью исключить из текста запроса либо формировать текст динамически.
Проанализируем распространенную ошибку производительности при обходе выборки результата запроса:
// Антипаттерн: поиск ссылки в цикле по каждой строке
Пока Выборка.Следующий() Цикл
Если Выборка.Категория = Справочники.КВ_КатегорииСотрудников.НайтиПоКоду("000000001") Тогда
// обработка данных
КонецЕсли;
КонецЦикла;
Вызов метода НайтиПоКоду() внутри цикла выполняет отдельный запрос к базе данных на каждую запись выборки. Если в выборке тысячи строк, формирование отчета катастрофически замедлится.
Для исправления ситуации воспользуемся одним из следующих подходов:
КатегорияЭталон = Справочники.КВ_КатегорииСотрудников.НайтиПоКоду("000000001");
Пока Выборка.Следующий() Цикл
Если Выборка.Категория = КатегорияЭталон Тогда
// обработка данных без лишних обращений к базе
КонецЕсли;
КонецЦикла;
Ручное создание отчетов через объект ТаблицаЗначений, написание циклов выгрузки и ручная привязка параметров в современных версиях «1С:Предприятие 8» являются устаревшим подходом. Для построения списков численности сотрудников и кадровых отчетов рекомендуется использовать Систему компоновки данных (СКД).
Преимущества использования СКД: