Как правильно получать и изменять данные через точку в 1С: Сравнение производительности и использование БСП

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

В процессе разработки на платформе «1С:Предприятие» каждый программист неизбежно сталкивается с необходимостью получения данных из связанных справочников, документов или регистров. Простейший и наиболее интуитивный путь — обратиться к нужному полю через точку, например: Источник.Касса.СчетУчетаКасс. Однако за этой простотой скрываются серьезные подводные камни, связанные с производительностью системы, неявными запросами к базе данных и потенциальными ошибками блокировок.

В этой статье мы подробно разберем, что происходит «под капотом» платформы при разыменовании ссылок через точку, в чем разница между объектным обращением и использованием функций Библиотеки стандартных подсистем (БСП), и как правильно изменять связанные данные, чтобы избежать критических ошибок выполнения.

Раздел 1. Механизм кэширования объектов и обращение через точку на уровне СУБД

Для начала проанализируем, как платформа «1С:Предприятие» обрабатывает обращение к реквизитам ссылочного типа. Когда мы пишем Источник.Касса, платформа получает только ссылку — уникальный идентификатор (GUID) объекта в базе данных. В этот момент обращения к таблице касс в СУБД не происходит.

Но как только мы делаем следующий шаг и пишем Источник.Касса.СчетУчетаКасс, происходит так называемое разыменование ссылки. Давайте разберем по шагам, что при этом делает платформа:

  1. Платформа проверяет, есть ли данные этого объекта (в данном случае конкретной кассы) в локальном кэше объектов на сервере 1С.
  2. Если данных в кэше нет, платформа выполняет запрос к СУБД. При этом — и это критически важно понять — из базы данных считывается не один конкретный реквизит СчетУчетаКасс, а весь объект целиком. Будут считаны все его простые реквизиты, табличные части и служебные поля.
  3. Полученный объект помещается в кэш (время жизни которого по умолчанию составляет до 20 минут, либо пока он не будет вытеснен другими данными или очищен принудительно).

В чем главная опасность такого поведения? Если у вашего связанного объекта десятки реквизитов, тяжелые табличные части или реквизиты типа ХранилищеЗначения (например, файлы или картинки), то копеечное обращение за одним реквизитом превращается в прокачку огромного объема избыточных данных между СУБД, сервером приложений 1С и клиентом. Если подобный код выполняется внутри цикла (например, при проведении пачки документов), производительность системы лавинообразно падает, а сервер начинает испытывать жесткий дефицит оперативной памяти.

Раздел 2. Сравнение подходов: Объектная техника против функций БСП

Рассмотрим два варианта написания кода, которые часто вызывают споры среди разработчиков:

Вариант 1. Прямое обращение через точку


// Прямой вызов
РеквизитКассы = Источник.Касса.СчетУчетаКасс;

Вариант 2. Использование оптимизированной функции БСП


// Использование универсальной функции БСП
РеквизитКассы = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Источник.Касса, "СчетУчетаКасс");

Давайте сопоставим эти варианты по ключевым параметрам производительности и стабильности:

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

Раздел 3. Ошибка изменения данных: Поле объекта недоступно для записи

В процессе обсуждения на форумах часто возникает вопрос: можно ли использовать конструкцию присваивания вида:

Источник.Касса.СчетУчетаКасс = ПланыСчетов.Хозрасчетный.ЧековыеКнижки;

Ответ однозначный: нет, такой код вызовет критическую ошибку выполнения.

Давайте разберем причину этого поведения. Свойство Источник.Касса возвращает тип СправочникСсылка.Кассы. Любая ссылка в 1С принципиально доступна только для чтения. Мы не можем напрямую модифицировать реквизиты ссылки. Попытка записать значение в реквизит ссылки вызовет исключение «Поле объекта недоступно для записи».

Для того чтобы изменить реквизит связанного объекта, нам необходимо явно получить сам объект, изменить его реквизиты и записать его обратно в базу данных. Рассмотрим, как это сделать правильно:


// Шаг 1: Проверяем заполненность ссылки, чтобы избежать ошибки обращения к пустому значению
Если ЗначениеЗаполнено(Источник.Касса) Тогда
    
    // Шаг 2: Получаем объект из ссылки для возможности редактирования
    КассаОбъект = Источник.Касса.ПолучитьОбъект();
    
    // Шаг 3: Модифицируем нужный реквизит
    КассаОбъект.СчетУчетаКасс = ПланыСчетов.Хозрасчетный.ЧековыеКнижки;
    
    // Шаг 4: Записываем измененный объект в базу данных
    Попытка
        КассаОбъект.Записать();
    Исключение
        ВызватьИсключение "Не удалось записать изменения в кассу: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
    
КонецЕсли;

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

Раздел 4. Возможности БСП по работе с путями через точку

Современные версии Библиотеки стандартных подсистем (начиная с версий 3.x) значительно расширили возможности функций чтения реквизитов. Давайте посмотрим, как мы можем красиво и лаконично получать данные по сложным путям.

Если вам нужно получить реквизит не самой ссылки, а реквизит ее реквизита (например, узнать код счета учета кассы), вы можете передать в функцию БСП ЗначенияРеквизитовОбъекта строку, содержащую точки. Платформа самостоятельно преобразует этот путь в эффективный SQL-запрос с левым соединением (LEFT JOIN).

Рассмотрим пример использования:


// Получаем структуру реквизитов, используя путь через точку
ПараметрыКассы = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Источник.Касса, "СчетУчетаКасс.Код, СчетУчетаКасс.Наименование");

// Обращаемся к полученным значениям
// Обратите внимание: БСП заменяет точки в именах ключей результирующей структуры
КодСчета = ПараметрыКассы.СчетУчетаКассКод;
НаименованиеСчета = ПараметрыКассы.СчетУчетаКассНаименование;

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

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

Раздел 5. Использование точек в запросах 1С: Подводные камни

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

Давайте проанализируем пример. Предположим, у нас есть документ, у которого реквизит ДокументОснование имеет составной тип, включающий 15 различных видов документов. Если мы напишем в запросе:


ВЫБРАТЬ
    Регистратор.ДокументОснование.Дата КАК ДатаОснования
ИЗ
    РегистрНакопления.Продажи КАК Продажи

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

Как решить эту проблему? При обращении через точку от полей составного типа всегда необходимо использовать конструкцию ВЫРАЗИТЬ():


ВЫБРАТЬ
    ВЫРАЗИТЬ(Регистратор.ДокументОснование КАК Документ.РеализацияТоваровУслуг).Дата КАК ДатаОснования
ИЗ
    РегистрНакопления.Продажи КАК Продажи

Таким образом, мы жестко указываем платформе конкретную таблицу для соединения, исключая лишние неявные связи и обеспечивая мгновенное выполнение запроса.

Резюме и правила хорошего тона для разработчика

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

← На главную