Как правильно получить значение свойства объекта из регистра сведений «ЗначенияСвойствОбъектов» в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке в конфигурациях 1С, использующих классический механизм свойств и категорий (например, УТ 10.3, УПП 1.3 или КА 1.1), программисты часто сталкиваются с необходимостью программного извлечения значений дополнительных свойств для различных объектов — будь то номенклатура, контрагенты или договоры. Данные свойства хранятся в периодическом или непериодическом регистре сведений ЗначенияСвойствОбъектов.

Типичная проблема начинающих разработчиков заключается в том, что запрос к регистру возвращает пустой результат или значение Неопределено, хотя в пользовательском интерфейсе свойство визуально заполнено. В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно передавать параметры в запрос, а также рассмотрим альтернативные и современные методы получения свойств, оптимизацию производительности СУБД и работу с иерархией справочников.

Выявление ошибки: почему запрос возвращает пустой результат?

Рассмотрим исходный запрос, с которым столкнулся автор темы на форуме. Разработчик пытался получить значение свойства «Точка отгрузки» для родительской группы текущей номенклатуры:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|    РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|    ЗначенияСвойствОбъектов.Свойство = &Свойство
|    И ЗначенияСвойствОбъектов.Объект = &Объект";

Запрос.УстановитьПараметр("Свойство", ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Точка отгрузки", Истина));
Запрос.УстановитьПараметр("Объект", Справочники.Номенклатура.НайтиПоНаименованию(Номенклатура.Родитель.Наименование, Истина));

Проанализируем этот код и выясним причину сбоя. Главная ошибка кроется в строке установки параметра Объект. Разработчик выполняет поиск элемента справочника по наименованию родителя:

Справочники.Номенклатура.НайтиПоНаименованию(Номенклатура.Родитель.Наименование, Истина)

Почему этот подход ошибочен и опасен?

  1. Неуникальность наименований. Метод НайтиПоНаименованию ищет первое попавшееся совпадение в базе данных. Если в справочнике Номенклатура есть несколько папок или элементов с одинаковым названием (например, несколько групп «Материалы» в разных ветках дерева), система вернет ссылку на первую найденную группу, которая может не иметь никакого отношения к текущему товару.
  2. Избыточность поиска. Если у нас уже есть объект Номенклатура, то обращение к его реквизиту Номенклатура.Родитель возвращает готовую, полноценную ссылку типа СправочникСсылка.Номенклатура. Выполнять повторный поиск в базе данных по строковому представлению этой ссылки — абсолютно лишнее и вредное действие, снижающее производительность.
  3. Путаница между типами объектов. В старых конфигурациях свойства часто привязываются не к группам номенклатуры (папкам справочника Номенклатура), а к элементам справочника НоменклатурныеГруппы. Это совершенно разные объекты метаданных. При поиске по строке разработчик мог перепутать группу справочника и номенклатурную группу.

Способ 1. Исправление параметров запроса и прямая передача ссылок

Упростим код и сделаем его надежным. Вместо поиска по наименованию мы передадим готовую ссылку на родительскую группу напрямую в параметры запроса. Проанализируем правильный вариант заполнения параметров:


// Сначала найдем свойство (желательно использовать предопределенные элементы или поиск по коду, но если по наименованию — то делаем это один раз)
СвойствоСсылка = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Точка отгрузки", Истина);

Если ЗначениеЗаполнено(СвойствоСсылка) Тогда

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

КонецЕсли;

Способ 2. Отладка и проверка типов данных

Если запрос все равно не возвращает данные, выполним интерактивную проверку значений параметров перед запуском запроса. Для этого выведем диагностические сообщения или воспользуемся точкой останова в отладчике:


тСвойство = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Точка отгрузки", Истина);
Сообщить("Свойство: " + тСвойство + " | Тип: " + ТипЗнч(тСвойство));

тОбъект = Номенклатура.Родитель;
Сообщить("Объект: " + тОбъект + " | Тип: " + ТипЗнч(тОбъект));

Проанализировав вывод этих строк, мы сможем точно определить, найден ли нужный элемент справочника СвойстваОбъектов и заполнен ли родитель у номенклатуры (для корневых элементов родитель будет равен пустой ссылке).

Способ 3. Получение свойств с учетом иерархии (наследование свойств)

Часто в практических задачах свойства задаются не для конкретного товара, а для папки (группы), в которой он лежит. Если мы хотим получить свойство, назначенное либо самому товару, либо его родительской группе (или прародителю выше по дереву), нам нужно использовать проверку по иерархии.

В простейшем случае для поиска по иерархии в запросе можно использовать оператор В ИЕРАРХИИ:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    ЗначенияСвойствОбъектов.Объект КАК Объект,
|    ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|    РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|    ЗначенияСвойствОбъектов.Свойство = &Свойство
|    И ЗначенияСвойствОбъектов.Объект В ИЕРАРХИИ(&Объект)";

Запрос.УстановитьПараметр("Свойство", СвойствоСсылка);
Запрос.УстановитьПараметр("Объект", НоменклатураСсылка);

Важное предупреждение: Использование конструкции В ИЕРАРХИИ в условиях регистра сведений может вернуть несколько записей (например, если свойство задано и у самого товара, и у его папки). Чтобы корректно определить приоритет значения самого элемента над значением группы, потребуется построить более сложный запрос с получением уровня вложенности или использовать программный обход снизу вверх по родителям номенклатуры. Для генерации запроса с выборкой иерархии родителей всех уровней можно применить инструмент интерактивного формирования текста запроса к родителям справочника.

Альтернатива: Получение значения свойства в коде без написания запроса

Создавать объект Запрос целесообразно при обработке массивов данных или в сложных отчетах. Если же вам нужно быстро получить одно конкретное свойство для одного объекта в процедуре проведения документа или при открытии формы, гораздо эффективнее применить метод Получить() менеджера регистра сведений:


Отбор = Новый Структура;
Отбор.Вставить("Объект", НоменклатураСсылка);
Отбор.Вставить("Свойство", СвойствоСсылка);

// Получаем структуру ресурсов записи регистра напрямую
РесурсЗаписи = РегистрыСведений.ЗначенияСвойствОбъектов.Получить(Отбор);
ЗначениеСвойства = РесурсЗаписи.Значение;

Этот программный код работает быстрее классического запроса, легче читается и минимизирует нагрузку на СУБД за счет встроенных механизмов оптимизации платформы «1С:Предприятие» при работе с первичными ключами непериодических регистров.

Современный стандарт БСП: переход на дополнительные реквизиты и сведения

Если вы работаете в современных конфигурациях на базе Библиотеки Стандартных Подсистем (БСП), таких как УТ 11, ERP 2 или КА 2, то напрямую обращаться к таблицам свойств не рекомендуется. В БСП архитектура хранения дополнительных свойств разделена:

Для программного чтения таких свойств разработчики БСП предусмотрели готовые функции общего модуля УправлениеСвойствами. Рассмотрим пример получения свойства через БСП:


// Универсальное получение значения реквизита или сведения через БСП
ЗначениеСвойства = УправлениеСвойствами.ЗначениеСвойства(ОбъектСсылка, СвойствоСсылка);

Использование этого метода гарантирует, что ваш код сохранит работоспособность при обновлениях конфигурации, так как система сама определяет физическое место хранения свойства в базе данных.

Важные подводные камни при работе со свойствами объектов

Разберем две классические архитектурные ошибки, которые совершают программисты при проектировании запросов к регистру ЗначенияСвойствОбъектов.

1. Ошибка с потерей данных при «ЛЕВОМ СОЕДИНЕНИИ»

Когда требуется вывести список всей номенклатуры и рядом отобразить значение конкретного свойства, часто пишут следующий запрос:


// ОШИБОЧНЫЙ ЗАПРОС! Товары, у которых свойство не заполнено, исчезнут из выборки!
"ВЫБРАТЬ
|    Номенклатура.Ссылка КАК Товар,
|    ЗначенияСвойствОбъектов.Значение КАК ЗначениеСвойства
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|        ПО ЗначенияСвойствОбъектов.Объект = Номенклатура.Ссылка
|ГДЕ
|    ЗначенияСвойствОбъектов.Свойство = &Свойство"

Выясним причину проблемы: Наложение фильтра по соединяемой таблице в секции ГДЕ принудительно преобразует левое соединение во внутреннее (INNER JOIN). В результате из итоговой выборки полностью исключаются все элементы номенклатуры, у которых данное свойство вообще никогда не заполнялось.

Чтобы избежать этой проблемы, условие отбора по свойству необходимо перенести непосредственно в условие связи ПО:


// ПРАВИЛЬНЫЙ ЗАПРОС! Будут выведены все товары
"ВЫБРАТЬ
|    Номенклатура.Ссылка КАК Товар,
|    ЗначенияСвойствОбъектов.Значение КАК ЗначениеСвойства
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|        ПО ЗначенияСвойствОбъектов.Объект = Номенклатура.Ссылка
|        И ЗначенияСвойствОбъектов.Свойство = &Свойство"

2. Снижение производительности из-за составного типа данных ресурса «Значение»

Ресурс Значение в регистре ЗначенияСвойствОбъектов имеет составной тип данных (он может хранить числа, строки, булево, ссылки на различные справочники). При выполнении сложных запросов с соединениями по этому полю СУБД строит неоптимальные планы выполнения, так как ей приходится опрашивать таблицы всех возможных типов данных составного поля.

Для ускорения работы СУБД настоятельно рекомендуется явно приводить тип извлекаемого значения с помощью оператора ВЫРАЗИТЬ:


"ВЫБРАТЬ
|    Номенклатура.Ссылка КАК Товар,
|    ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК Справочник.ЗначенияСвойствОбъектов) КАК ЗначениеСвойства
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|        ПО ЗначенияСвойствОбъектов.Объект = Номенклатура.Ссылка
|        И ЗначенияСвойствОбъектов.Свойство = &Свойство"

Использование конструкции ВЫРАЗИТЬ позволяет СУБД обращаться напрямую к таблице конкретного справочника (в данном примере — вспомогательного справочника значений свойств), что в разы увеличивает скорость выполнения запроса в крупных информационных базах. Для автоматического поиска подобных неоптимальных конструкций и разыменований составных полей в коде можно использовать обработку для проверки запросов на разыменование полей составного типа.

← На главную