Разработка и Конфигурация · 11 мин
Как система 1С:Предприятие выполняет обращение к ссылочным данным
Как платформа 1С:Предприятие 8 работает со ссылками на объекты: разыменование через точку, запросы к ссылочным данным, типичные ошибки и рекомендации.
Ссылочные данные — основа любой конфигурации 1С:Предприятие 8. Справочники, документы, планы видов характеристик, планы счетов и другие объектные типы хранятся в базе данных так, что каждый объект имеет уникальную ссылку — стабильный идентификатор, по которому на него ссылаются другие объекты. Понимание того, как платформа обращается к данным через ссылку, критично для разработчика: от этого зависят производительность запросов, корректность обхода данных и отсутствие лишних обращений к серверу.
Ключевой момент: ссылка сама по себе не содержит реквизитов объекта. Это только указатель. Чтобы получить, например, наименование контрагента или валюту договора, платформа должна выполнить «разыменование» — прочитать данные объекта из базы по этой ссылке. Способов сделать это несколько: через точку в коде, через метод ПолучитьОбъект(), через запрос с соединением таблиц. Каждый способ имеет свою цену и область применения.
Ниже разобрано, как устроены ссылки на уровне хранения, какие механизмы обращения к ссылочным данным существуют во встроенном языке и в языке запросов, где возникают типичные ошибки и как проверить, что код не генерирует лишние обращения к базе.
Как устроена ссылка на уровне платформы и базы данных
Каждый ссылочный объект в 1С имеет уникальный идентификатор — GUID, который хранится в поле ссылки таблицы базы данных. Когда документ содержит реквизит «Контрагент», в его таблице фактически хранится только этот идентификатор, а не наименование и не ИНН контрагента. Наименование и прочие реквизиты лежат в таблице справочника «Контрагенты».
Такое разделение дает важное свойство: переименование контрагента или изменение его реквизитов не требует перезаписи всех документов, где он указан. Документы продолжают хранить ту же ссылку, а актуальные данные подтягиваются из справочника в момент обращения. Обратная сторона — каждое обращение к данным через ссылку потенциально означает дополнительное чтение из другой таблицы.
На уровне встроенного языка ссылка представлена типами вида СправочникСсылка.Контрагенты, ДокументСсылка.РеализацияТоваровУслуг и т.д. Значение ссылки можно сравнивать, передавать в процедуры, сохранять в реквизитах и переменных. Проверить, заполнена ли ссылка, можно функцией ЗначениеЗаполнено() или сравнением с пустой ссылкой, полученной через Справочники.Контрагенты.ПустаяСсылка().
Обращение к реквизитам через точку: как это работает и чем опасно
Самый простой способ получить данные ссылки — обратиться через точку: Контрагент.Наименование или Документ.Контрагент.ИНН. Платформа поддерживает «сквозное» обращение через цепочку ссылок, и при единичном доступе это удобно и читаемо. Платформа при этом неявно загружает объект из базы данных и кэширует его данные в рамках текущего контекста.
Проблема возникает в циклах. Если в цикле по 500 строкам табличной части выполняется обращение Строка.Номенклатура.Артикул, платформа может выполнить до 500 отдельных чтений из базы (в клиент-серверном варианте — 500 обращений к серверу СУБД). Это классическая причина «тормозов» при проведении документов и формировании отчетов.
Второй подводный камень — обращение через точку к ссылке, объекта для которой нет в базе. Такая ситуация называется «битая ссылка» и возникает, например, после удаления объекта без контроля ссылочной целостности или при проблемах обмена. В зависимости от контекста обращение вернет ошибку или неопределенное поведение, поэтому перед разыменованием сомнительных ссылок стоит выполнять проверку.
Получение объекта методом ПолучитьОбъект(): когда он действительно нужен
Метод Ссылка.ПолучитьОбъект() загружает объект из базы целиком и возвращает значение типа СправочникОбъект или ДокументОбъект. В отличие от чтения реквизитов через точку, объект предназначен для изменения данных: после правки реквизитов вызывается метод Записать(), и изменения сохраняются в базе с контролем прав, подписками на события и проверками заполнения.
Важное ограничение: ПолучитьОбъект() — дорогая операция. Загружается объект целиком, включая все табличные части, даже если нужно поменять один реквизит. Для чтения данных этот метод использовать не нужно вообще — для чтения достаточно обращения через точку или запроса. Вызов ПолучитьОбъект() только ради чтения наименования — типичная ошибка начинающих разработчиков, замедляющая код в разы.
Для частичной модификации без загрузки всего объекта в платформе есть альтернативы: установка реквизитов через запрос с конструкцией ВЫБРАТЬ ... ИЗ с последующей записью не поддерживается напрямую, но можно использовать метод УстановитьЗначениеРеквизита у специализированных механизмов или писать небольшие объекты через объектную модель с минимальным набором данных. В простых сценариях честный ПолучитьОбъект() + Записать() остается безопасным вариантом, если он не в цикле по тысячам элементов.
// Чтение одного реквизита — достаточно точки
Артикул = НоменклатураСсылка.Артикул;
// Изменение данных — нужен объект
ТоварОбъект = НоменклатураСсылка.ПолучитьОбъект();
ТоварОбъект.Артикул = "НовыйАртикул";
ТоварОбъект.Записать();Доступ к ссылочным данным в запросах: правильный способ массового чтения
Язык запросов 1С позволяет обращаться к реквизитам через точку прямо в тексте запроса: ВЫБРАТЬ Товары.Номенклатура.Артикул ИЗ Документ.Реализация.Товары КАК Товары. Платформа при компиляции запроса сама строит соединение с таблицей справочника, и вся выборка выполняется одним обращением к СУБД. Это принципиально отличается от разыменования в коде: никаких построчных чтений не происходит.
Для явного контроля над планом запроса рекомендуется писать соединения вручную через ЛЕВОЕ СОЕДИНЕНИЕ по условию равенства ссылок. Это делает запрос предсказуемым и упрощает анализ производительности. Обращение через точку в запросе допустимо, но при глубоких цепочках (ссылка на ссылку на ссылку) неявные соединения могут порождать тяжелые планы выполнения.
Отдельный прием — обращение к предопределенным данным и к представлению ссылки. Функция ПРЕДСТАВЛЕНИЕ(Ссылка) в запросе возвращает строковое представление объекта, что удобно для вывода, но не подходит для отборов и сортировок по смысловым полям. Для отбора всегда используйте саму ссылку или конкретный реквизит.
- 1Найдите в цикле обращения вида Строка.Ссылка.Реквизит
- 2Составьте запрос к основной таблице с соединением к таблице справочника по полю ссылки
- 3Выберите в запросе все нужные реквизиты через соединенную таблицу
- 4Выполните запрос один раз до цикла и обойдите выборку
- 5Проверьте время выполнения на реальном объеме данных
Проверка ссылок: пустые, битые и сравнение значений
Перед обращением к данным ссылки полезно убедиться, что она корректна. Пустую ссылку проверяют через ЗначениеЗаполнено() или сравнение с ПустаяСсылка() соответствующего менеджера. В запросах пустую ссылку передают параметром через ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).
Битая ссылка — ссылка, идентификатор которой отсутствует в таблице объекта. В запросе такие записи выявляются соединением: если после ЛЕВОГО СОЕДИНЕНИЯ поле соединенной таблицы равно NULL, объект по ссылке не существует. В коде битую ссылку можно определить проверкой Ссылка.ПолучитьОбъект() = Неопределено, но это дорого; легче использовать запрос или функцию ОбщегоНазначения.СсылкаСуществует из библиотеки стандартных подсистем, если она есть в конфигурации.
Сравнение ссылок оператором «=» выполняется по идентификатору, а не по содержимому — две ссылки на один объект равны, даже если получены разными способами. Это быстро и безопасно. А вот сравнение по наименованию или коду вместо ссылки — распространенная ошибка: наименования могут совпадать или меняться, и логика разваливается.
Как проверить корректность работы со ссылками
Одно обращение к СУБД на всю выборку
В замере производительности видны сотни одинаковых мелких запросов
Все строки основной таблицы имеют соответствие, поля справочника заполнены
В полях справочника NULL — в данных битые ссылки
ПолучитьОбъект() вызывается только для объектов, которые реально изменяются
ПолучитьОбъект() вызывается для чтения одного реквизита
Особенности в клиент-серверном варианте и вне сервера
В файловом варианте разыменование ссылки — это чтение из локального файла базы, и стоимость операции ниже, хотя лишние чтения все равно замедляют код. В клиент-серверном варианте каждое обращение через точку, выполненное на клиенте, может порождать серверный вызов, а на сервере — запрос к СУБД. Поэтому тонкий клиент с разыменованием ссылок в цикле — худший из сценариев.
Правильная архитектура: вся работа со ссылочными данными выполняется в серверных процедурах, на клиент передаются уже готовые значения или структуры данных. Контекстные серверные вызовы с передачей ссылки допустимы, но массовые операции должны выполняться пакетно — одним запросом или одной серверной процедурой, обрабатывающей массив ссылок.
Также учитывайте кэширование: платформа кэширует прочитанные данные объектов в рамках сеанса, поэтому повторное обращение к той же ссылке дешевле первого. Однако рассчитывать на кэш в проектировании нельзя — его поведение не гарантирует производительность при больших объемах и параллельной работе.
Частые вопросы
Чем отличается СправочникСсылка от СправочникОбъект?
Ссылка — это только идентификатор объекта, предназначенный для хранения в реквизитах, сравнений и передачи между процедурами. Объект — это загруженные в память данные со всеми реквизитами и табличными частями, предназначенные для чтения и изменения с последующей записью. Для чтения одного поля объект получать не нужно.
Почему запрос с обращением через точку иногда работает медленно?
Каждое обращение через точку в запросе порождает неявное соединение с таблицей справочника. При длинных цепочках ссылок таких соединений становится много, и план запроса на СУБД усложняется. Явные соединения через ЛЕВОЕ СОЕДИНЕНИЕ с отбором только нужных полей обычно дают более предсказуемую производительность.
Как найти все битые ссылки в базе?
Стандартный способ — запрос с ЛЕВЫМ СОЕДИНЕНИЕМ таблицы, содержащей ссылку, к таблице объекта, с условием, что поле ссылки в соединенной таблице ЕСТЬ NULL, а сама ссылка не пустая. Для полной проверки базы используйте штатную обработку тестирования и исправления информационной базы с проверкой ссылочной целостности.
Можно ли изменить реквизит объекта без ПолучитьОбъект()?
Напрямую через ссылку — нет: запись данных выполняется только через объектную модель (ПолучитьОбъект + Записать) или через наборы записей для регистров. Попытки обойти это прямыми правками в СУБД нарушают лицензионные ограничения и ломают целостность данных.
Безопасно ли передавать ссылки между клиентом и сервером?
Да, ссылки сериализуются и свободно передаются между клиентом и сервером. Небезопасна другая практика — передавать на клиент объект (СправочникОбъект): объектные типы недоступны на тонком клиенте, и попытка такой передачи вызовет ошибку.
Что возвращает ПРЕДСТАВЛЕНИЕ(Ссылка) в запросе?
Строковое представление объекта — обычно наименование для справочника или номер с датой для документа. Это удобно для вывода в отчет, но представление нельзя использовать для отборов и соединений: для этого служит сама ссылка.
Что важно запомнить
Ссылка — это идентификатор, а не данные. Единичное чтение реквизита допустимо через точку, массовое чтение — только через запрос с соединением таблиц. ПолучитьОбъект() используется исключительно для изменения данных. Проверяйте ссылки на пустоту и существование, а производительность — замером на реальном объеме.