В среде разработчиков 1С часто возникают споры об эффективности использования стандартных функций Библиотеки стандартных подсистем (БСП). Один из наиболее частых вопросов касается целесообразности применения функции ОбщегоНазначения.ЗначенияРеквизитовОбъекта(). На первый взгляд, простые замеры производительности могут показать, что обращение к реквизитам «через точку» от ссылки работает быстрее или так же. Однако за этим скрываются важные архитектурные особенности платформы 1С:Предприятие и СУБД, которые мы подробно разберем в этой статье.
Мы проанализируем, почему стандартные тесты производительности «в лоб» часто оказываются невалидными, как платформа работает с памятью и базой данных при разных способах обращения к реквизитам, а также рассмотрим недокументированные и продвинутые возможности функций БСП для оптимизации кода.
Рассмотрим ситуацию, с которой часто сталкиваются начинающие разработчики. При проведении замера производительности пишется следующий код:
// Некорректный тест производительности
КонтрагентРекв = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ЭтотОбъект.Ссылка, "Контрагент");
Контрагент1 = КонтрагентРекв.Контрагент;
Контрагент2 = ЭтотОбъект.Контрагент;
Замер времени показывает, что обращение к ЭтотОбъект.Контрагент выполняется в разы быстрее, чем вызов функции БСП. На этом основании делается ошибочный вывод, что функция БСП бесполезна и только замедляет работу системы. Выясним причину этой иллюзии.
В данном тесте сравниваются абсолютно разные по своей сути операции:
ЭтотОбъект.Контрагент данные считываются напрямую из оперативной памяти сервера приложений, так как объект ЭтотОбъект уже полностью прочитан из базы данных и находится в памяти. Разумеется, это происходит мгновенно.ЗначенияРеквизитовОбъекта() платформа вынуждена сформировать SQL-запрос, отправить его СУБД, дождаться выполнения, получить результат, преобразовать его в структуру 1С и вернуть разработчику. Это требует накладных расходов на сериализацию и сетевое взаимодействие.Чтобы сравнение было честным и валидным, необходимо сопоставлять получение данных из базы данных через ссылку вне контекста уже открытого объекта:
// Валидное сравнение получения данных из БД
// Вариант 1: Через точку от ссылки
Контрагент1 = СсылкаНаОбъект.Контрагент;
// Вариант 2: Через функцию БСП
КонтрагентРекв = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаОбъект, "Контрагент");
Контрагент2 = КонтрагентРекв.Контрагент;
Проанализируем, что происходит на уровне платформы и СУБД в каждом из этих вариантов.
Для того чтобы понять разницу, рассмотрим физический смысл обращения СсылкаНаОбъект.Контрагент. Когда мы обращаемся к реквизиту через точку от ссылки, платформа 1С выполняет следующие действия:
SELECT * FROM ..., считывая абсолютно все реквизиты объекта (за исключением некоторых тяжелых типов вроде неограниченных строк и хранилищ значения, которые могут считываться по требованию).Если наш справочник или документ содержит всего 2–3 реквизита и не имеет табличных частей, разница в скорости будет неощутима. Но представьте, что мы обращаемся к реквизиту «Дата» или «Проведен» тяжелого документа (например, «Реализация товаров и услуг»), который содержит сотни реквизитов шапки, десятки реквизитов в пяти табличных частях и тяжелые служебные поля. При простом обращении Ссылка.Проведен платформа поднимет в память сервера весь этот гигантский документ. Это приводит к избыточному расходу оперативной памяти и создает колоссальную нагрузку на дисковую подсистему и сеть между сервером 1С и сервером СУБД.
При использовании функции ЗначенияРеквизитовОбъекта() под капотом БСП динамически конструирует и выполняет оптимальный SQL-запрос, который извлекает только те поля, которые были явно указаны в параметрах. СУБД мгновенно находит нужную запись по индексу первичного ключа и возвращает обратно тонкий пакет данных.
Рассмотрим наглядный пример. В объекте присутствует реквизит с типом ХранилищеЗначения, в котором хранится прикрепленный файл (например, PDF-документ или архив на 50 Мегабайт). При обращении через точку от ссылки система может попытаться прочитать или заблокировать лишние ресурсы, в то время как точечный запрос через функцию БСП гарантирует, что тяжелые данные вообще не будут затронуты.
Использование функции БСП критически важно в следующих ситуациях:
Большинство разработчиков привыкли использовать эту функцию только для базового чтения реквизитов по их именам. Однако БСП предоставляет мощный функционал, о котором часто забывают.
1. Передача имени предопределенного элемента строкой вместо ссылки
В качестве первого параметра Ссылка можно передать не только физическую ссылку из базы данных, но и строковое представление предопределенного элемента, например, "Справочник.Валюты.СистемнаяВалюта". Функция БСП самостоятельно преобразует строку в ссылку с помощью метода платформы ПредопределенныйЭлемент(), выполнит запрос и вернет запрошенные данные. Это избавляет от написания лишних строк кода для поиска ссылки.
2. Использование структуры для задания псевдонимов (алиасов) реквизитов
Если вам нужно получить значения реквизитов, но в результирующей структуре вы хотите видеть другие ключи (например, для последующей передачи в функции, ожидающие строго определенные имена свойств), вы можете передать в параметр Реквизиты структуру:
Рассмотрим пример переименования полей на лету:
ИменаПолей = Новый Структура;
ИменаПолей.Вставить("ПолучательСсылка", "Контрагент");
ИменаПолей.Вставить("НалоговыйНомер", "ИНН");
Результат = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаКонтрагента, ИменаПолей);
// На выходе получим структуру с ключами "ПолучательСсылка" и "НалоговыйНомер"
3. Чтение вложенных реквизитов через точку (LEFT JOIN)
Функция БСП поддерживает чтение реквизитов связанных объектов через точку. Например, нам нужно получить ИНН основного банковского счета контрагента. Мы можем написать так:
Реквизиты = "Контрагент.ОсновнойБанковскийСчет.НомерСчета, Контрагент.ИНН";
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаДоговор, Реквизиты);
В данном случае БСП автоматически сформирует запрос с левым соединением (LEFT JOIN) таблиц базы данных и вернет структуру с соответствующими полями. Это гораздо быстрее и безопаснее, чем последовательное разыменование ссылок через точку в коде.
4. Встроенная поддержка мультиязычности
Функция имеет необязательный четвертый параметр КодЯзыка. Если в вашей конфигурации используются мультиязычные реквизиты (например, наименования товаров на разных языках), вы можете передать туда код локализации (например, "en"), и функция вернет значение реквизита для указанного языка. Если параметр не заполнен, вернется значение для текущего языка сеанса.
5. Безопасная обработка пустых и «битых» ссылок
Если вы попытаетесь обратиться через точку к удаленному объекту («битой» ссылке) или к пустой ссылке (например, Договор.Владелец, когда договор не заполнен), система вернет Неопределено или вызовет ошибку в зависимости от контекста выполнения. Функция ЗначенияРеквизитовОбъекта() обрабатывает такие ситуации безопасно:
Неопределено.6. Контроль прав доступа на уровне записей (RLS)
Третий параметр функции ВыбратьРазрешенные (типа Булево, по умолчанию Ложь) управляет поведением запроса при включенном RLS:
Истина, запрос к СУБД будет выполняться с использованием конструкции РАЗРЕШЕННЫЕ. Если у пользователя нет прав на чтение данной записи, функция вернет структуру со значениями Неопределено вместо вызова исключения ограничения прав.Ложь, при отсутствии прав у пользователя на чтение таблицы или конкретного реквизита система мгновенно вызовет ошибку доступа.Если вам необходимо прочитать реквизиты не одного объекта, а целого списка (массива) однотипных объектов, использовать ЗначенияРеквизитовОбъекта() в цикле категорически запрещено — это классическая ошибка «запрос в цикле». Для этих целей в БСП предусмотрена парная функция ЗначенияРеквизитовОбъектов().
Рассмотрим правила ее использования:
Соответствие (Map), в котором ключом выступает сама ссылка на объект, а значением — структура прочитанных реквизитов.Посмотрим на практический пример использования групповой функции:
// Подготовим массив ссылок одного типа
МассивКонтрагентов = Новый Массив;
МассивКонтрагентов.Добавить(СсылкаКонтрагент1);
МассивКонтрагентов.Добавить(СсылкаКонтрагент2);
// Получим реквизиты одной выборкой к БД
ТаблицаРеквизитов = "ИНН, КПП, ЮридическийАдрес";
СоответствиеДанных = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(МассивКонтрагентов, ТаблицаРеквизитов);
// Обходим результат
Для Каждого ЭлементСоответствия Из СоответствиеДанных Цикл
ТекущаяСсылка = ЭлементСоответствия.Ключ;
РеквизитыОбъекта = ЭлементСоответствия.Value; // Структура с ИНН, КПП, ЮридическийАдрес
Сообщить("Контрагент: " + ТекущаяСсылка + ", ИНН: " + РеквизитыОбъекта.ИНН);
КонецЦикла;
Для закрепления материала обобщим рекомендации по выбору инструмента для получения данных из базы 1С:
ЭтотОбъект), получайте реквизиты напрямую из него. Это самый быстрый способ.Ссылка.ИмяРеквизита. Платформа один раз прочитает его в кэш, и последующие обращения в рамках этого же сеанса будут быстрыми.ОбщегоНазначения.ЗначенияРеквизитовОбъекта().ОбщегоНазначения.ЗначенияРеквизитовОбъектов(), передавая в него накопленный массив ссылок.Использование функций БСП для чтения реквизитов — это признак профессиональной разработки на платформе 1С:Предприятие, гарантирующий стабильную и быструю работу вашей конфигурации под любыми нагрузками.