В среде разработчиков 1С часто возникают споры о том, насколько допустимо обращение к реквизитам объекта через точку от ссылки. На первый взгляд, конструкция вида КонтрагентСсылка.ИНН выглядит лаконично и понятно. Однако за этой простотой скрываются серьезные механизмы платформы, которые при неправильном использовании могут привести к катастрофическому падению производительности и «утечкам» оперативной памяти сервера приложений. Рассмотрим подробно, что происходит «под капотом» платформы, и разберем лучшие практики оптимизации кода.
Когда мы пишем ИНН = КонтрагентСсылка.ИНН;, платформа 1С не просто запрашивает одно поле из таблицы базы данных. На уровне SQL-запроса выполняется команда SELECT * FROM ... WHERE ID = .... Это означает, что из базы данных извлекаются абсолютно все реквизиты объекта: примитивные типы (строки, числа), ссылки на другие объекты, а также инициализируются структуры для работы с табличными частями.
Проанализируем последствия такого подхода:
rphost. При массовой обработке данных в цикле это приводит к быстрому расходу оперативной памяти.Одним из аргументов в пользу «точки» является механизм кэширования (так называемый L2-кэш объектов). Выясним, как он работает. Когда объект считывается через точку впервые, он попадает в кэш сервера приложений на определенное время (по умолчанию около 20 минут). Если в этом же сеансе вы снова обратитесь к любому реквизиту этой же ссылки, данные будут взяты из памяти, и повторного запроса к СУБД не будет.
Рассмотрим пример, где точка может быть эффективнее запроса:
// Если в цикле 1000 итераций, но всего 5 уникальных счетов
Для Каждого Проводка Из Проводки Цикл
Если Проводка.Счет.Количественный Тогда // Счет закэшируется после первого обращения
// Логика обработки
КонецЕсли;
КонецЦикла;
В этой ситуации, если уникальных ссылок мало, кэш помогает избежать лишних запросов. Однако, если в цикле перебираются тысячи разных ссылок, кэш начинает «вымываться». Платформа вынуждена постоянно удалять старые объекты из памяти, чтобы записать новые, что порождает колоссальную нагрузку на дисковую подсистему и процессор.
Самая распространенная ошибка — использование обращения через точку в теле цикла по большой выборке данных. В программировании это называется проблемой «N+1». Вместо того чтобы получить данные для всех 100 записей одним запросом, система выполняет 100 отдельных запросов к базе данных.
Посмотрим на пример крайне неоптимального кода, который часто встречается в «самописных» обменах данными:
Для Каждого Док Из МассивДокументов Цикл
// Здесь Док.Ссылка - это обращение через точку в цикле
Для Каждого Рекв Из Док.Ссылка.Метаданные.Реквизиты Цикл
Если Док[Рекв.Имя] <> Док.Ссылка[Рекв.Имя] Тогда
Изменен = Истина;
КонецЕсли;
КонецЦикла;
КонецЦикла;
В данном случае, если в массиве 100 документов, а у каждого по 20 реквизитов, система выполнит как минимум 100 полных чтений объектов из базы. Если документы тяжелые (с большими табличными частями), это приведет к зависанию сеанса.
Для получения значений одного или нескольких реквизитов без создания тяжелого объекта в памяти, в Библиотеке Стандартных Подсистем (БСП) предусмотрены специальные функции. Самая популярная из них — ОбщегоНазначения.ЗначениеРеквизитаОбъекта.
Рассмотрим, почему это лучше:
ВЫБРАТЬ Реквизит ИЗ Таблица ГДЕ Ссылка = &Ссылка. СУБД возвращает только нужные данные.rphost не забивается ненужными данными других полей.Пример правильного получения данных:
// Получаем один реквизит
ИНН = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаКонтрагента, "ИНН");
// Получаем сразу несколько реквизитов (возвращает структуру)
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(СсылкаНаНоменклатура, "Артикул, НаименованиеПолное, СтавкаНДС");
Несмотря на удобство БСП, функции общего модуля остаются «одиночными» вызовами. Если вам нужно получить данные для списка ссылок, использование ОбщегоНазначения.ЗначениеРеквизитаОбъекта в цикле будет почти так же плохо, как и обращение через точку (так как это все равно будет множество мелких запросов к СУБД).
Разберем алгоритм правильной работы с массивом данных:
ГДЕ Ссылка В (&МассивСсылок).Проанализируем пример оптимизации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// Теперь данные можно быстро найти, не обращаясь к базе в цикле
Для Каждого Строка Из ТаблицаДанных Цикл
// Работаем с данными из таблицы
КонецЦикла;
Подведем итог нашему исследованию. Обращение через точку — это не «запрещенный» прием, а инструмент, который требует понимания контекста. Рассмотрим финальные рекомендации:
Объектом (например, в модуле объекта), так как он уже прочитан из базы и находится в памяти.ЗначениеРеквизитаОбъекта), если нужно получить 1–2 поля одной ссылки вне цикла.Документ.Контрагент.ОсновнойМенеджер.ФизЛицо.ФИО. Каждый переход через точку в такой цепочке — это отдельный тяжелый запрос к базе данных.Соблюдение этих простых правил позволит вашим конфигурациям работать быстро даже на очень больших объемах данных, сохраняя стабильность сервера приложений и комфорт пользователей.