К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как система 1С:Предприятие выполняет обращение к ссылочным данным

Как платформа 1С:Предприятие 8 работает со ссылками на объекты: разыменование через точку, запросы к ссылочным данным, типичные ошибки и рекомендации.

Ссылочные данные — основа любой конфигурации 1С:Предприятие 8. Справочники, документы, планы видов характеристик, планы счетов и другие объектные типы хранятся в базе данных так, что каждый объект имеет уникальную ссылку — стабильный идентификатор, по которому на него ссылаются другие объекты. Понимание того, как платформа обращается к данным через ссылку, критично для разработчика: от этого зависят производительность запросов, корректность обхода данных и отсутствие лишних обращений к серверу.

Ключевой момент: ссылка сама по себе не содержит реквизитов объекта. Это только указатель. Чтобы получить, например, наименование контрагента или валюту договора, платформа должна выполнить «разыменование» — прочитать данные объекта из базы по этой ссылке. Способов сделать это несколько: через точку в коде, через метод ПолучитьОбъект(), через запрос с соединением таблиц. Каждый способ имеет свою цену и область применения.

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

Как устроена ссылка на уровне платформы и базы данных

Каждый ссылочный объект в 1С имеет уникальный идентификатор — GUID, который хранится в поле ссылки таблицы базы данных. Когда документ содержит реквизит «Контрагент», в его таблице фактически хранится только этот идентификатор, а не наименование и не ИНН контрагента. Наименование и прочие реквизиты лежат в таблице справочника «Контрагенты».

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

На уровне встроенного языка ссылка представлена типами вида СправочникСсылка.Контрагенты, ДокументСсылка.РеализацияТоваровУслуг и т.д. Значение ссылки можно сравнивать, передавать в процедуры, сохранять в реквизитах и переменных. Проверить, заполнена ли ссылка, можно функцией ЗначениеЗаполнено() или сравнением с пустой ссылкой, полученной через Справочники.Контрагенты.ПустаяСсылка().

Обращение к реквизитам через точку: как это работает и чем опасно

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

Проблема возникает в циклах. Если в цикле по 500 строкам табличной части выполняется обращение Строка.Номенклатура.Артикул, платформа может выполнить до 500 отдельных чтений из базы (в клиент-серверном варианте — 500 обращений к серверу СУБД). Это классическая причина «тормозов» при проведении документов и формировании отчетов.

Второй подводный камень — обращение через точку к ссылке, объекта для которой нет в базе. Такая ситуация называется «битая ссылка» и возникает, например, после удаления объекта без контроля ссылочной целостности или при проблемах обмена. В зависимости от контекста обращение вернет ошибку или неопределенное поведение, поэтому перед разыменованием сомнительных ссылок стоит выполнять проверку.

Получение объекта методом ПолучитьОбъект(): когда он действительно нужен

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

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

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

1c
// Чтение одного реквизита — достаточно точки
Артикул = НоменклатураСсылка.Артикул;

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

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

Язык запросов 1С позволяет обращаться к реквизитам через точку прямо в тексте запроса: ВЫБРАТЬ Товары.Номенклатура.Артикул ИЗ Документ.Реализация.Товары КАК Товары. Платформа при компиляции запроса сама строит соединение с таблицей справочника, и вся выборка выполняется одним обращением к СУБД. Это принципиально отличается от разыменования в коде: никаких построчных чтений не происходит.

Для явного контроля над планом запроса рекомендуется писать соединения вручную через ЛЕВОЕ СОЕДИНЕНИЕ по условию равенства ссылок. Это делает запрос предсказуемым и упрощает анализ производительности. Обращение через точку в запросе допустимо, но при глубоких цепочках (ссылка на ссылку на ссылку) неявные соединения могут порождать тяжелые планы выполнения.

Отдельный прием — обращение к предопределенным данным и к представлению ссылки. Функция ПРЕДСТАВЛЕНИЕ(Ссылка) в запросе возвращает строковое представление объекта, что удобно для вывода, но не подходит для отборов и сортировок по смысловым полям. Для отбора всегда используйте саму ссылку или конкретный реквизит.

Как переписать цикл с разыменованием на запрос
  1. 1
    Найдите в цикле обращения вида Строка.Ссылка.Реквизит
  2. 2
    Составьте запрос к основной таблице с соединением к таблице справочника по полю ссылки
  3. 3
    Выберите в запросе все нужные реквизиты через соединенную таблицу
  4. 4
    Выполните запрос один раз до цикла и обойдите выборку
  5. 5
    Проверьте время выполнения на реальном объеме данных

Проверка ссылок: пустые, битые и сравнение значений

Перед обращением к данным ссылки полезно убедиться, что она корректна. Пустую ссылку проверяют через ЗначениеЗаполнено() или сравнение с ПустаяСсылка() соответствующего менеджера. В запросах пустую ссылку передают параметром через ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).

Битая ссылка — ссылка, идентификатор которой отсутствует в таблице объекта. В запросе такие записи выявляются соединением: если после ЛЕВОГО СОЕДИНЕНИЯ поле соединенной таблицы равно NULL, объект по ссылке не существует. В коде битую ссылку можно определить проверкой Ссылка.ПолучитьОбъект() = Неопределено, но это дорого; легче использовать запрос или функцию ОбщегоНазначения.СсылкаСуществует из библиотеки стандартных подсистем, если она есть в конфигурации.

Сравнение ссылок оператором «=» выполняется по идентификатору, а не по содержимому — две ссылки на один объект равны, даже если получены разными способами. Это быстро и безопасно. А вот сравнение по наименованию или коду вместо ссылки — распространенная ошибка: наименования могут совпадать или меняться, и логика разваливается.

Контрольные точки

Как проверить корректность работы со ссылками

Запрос к табличной части с реквизитами справочника
Норма

Одно обращение к СУБД на всю выборку

Тревожный признак

В замере производительности видны сотни одинаковых мелких запросов

Соединение по ссылке в запросе
Норма

Все строки основной таблицы имеют соответствие, поля справочника заполнены

Тревожный признак

В полях справочника NULL — в данных битые ссылки

Обработка записи объектов в цикле
Норма

ПолучитьОбъект() вызывается только для объектов, которые реально изменяются

Тревожный признак

ПолучитьОбъект() вызывается для чтения одного реквизита

Особенности в клиент-серверном варианте и вне сервера

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

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

Также учитывайте кэширование: платформа кэширует прочитанные данные объектов в рамках сеанса, поэтому повторное обращение к той же ссылке дешевле первого. Однако рассчитывать на кэш в проектировании нельзя — его поведение не гарантирует производительность при больших объемах и параллельной работе.

Типичные ошибки при обращении к ссылочным данным
01
Разыменование ссылки в цикле по строкам — сотни лишних обращений к базе и резкое падение производительности
02
Вызов ПолучитьОбъект() ради чтения реквизита — загружается весь объект с табличными частями, хотя нужно одно поле
03
Сравнение объектов по наименованию вместо ссылки — логика ломается при переименовании или дублях
04
Отсутствие проверки на битые ссылки — ошибки при разыменовании после обменов и удалений
05
Обращение через точку к цепочке ссылок на клиенте — каскад серверных вызовов в тонком клиенте

Частые вопросы

Чем отличается СправочникСсылка от СправочникОбъект?

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

Почему запрос с обращением через точку иногда работает медленно?

Каждое обращение через точку в запросе порождает неявное соединение с таблицей справочника. При длинных цепочках ссылок таких соединений становится много, и план запроса на СУБД усложняется. Явные соединения через ЛЕВОЕ СОЕДИНЕНИЕ с отбором только нужных полей обычно дают более предсказуемую производительность.

Как найти все битые ссылки в базе?

Стандартный способ — запрос с ЛЕВЫМ СОЕДИНЕНИЕМ таблицы, содержащей ссылку, к таблице объекта, с условием, что поле ссылки в соединенной таблице ЕСТЬ NULL, а сама ссылка не пустая. Для полной проверки базы используйте штатную обработку тестирования и исправления информационной базы с проверкой ссылочной целостности.

Можно ли изменить реквизит объекта без ПолучитьОбъект()?

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

Безопасно ли передавать ссылки между клиентом и сервером?

Да, ссылки сериализуются и свободно передаются между клиентом и сервером. Небезопасна другая практика — передавать на клиент объект (СправочникОбъект): объектные типы недоступны на тонком клиенте, и попытка такой передачи вызовет ошибку.

Что возвращает ПРЕДСТАВЛЕНИЕ(Ссылка) в запросе?

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

Итог

Что важно запомнить

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