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