Ссылки в 1С Предприятие: что внутри и как с ними обращаться
Содержание
Ссылка в 1С — это не сам элемент справочника или документ, а его постоянный адрес: внутри лежит уникальный идентификатор объекта в базе. Реквизиты по ссылке при каждом обращении читаются из базы заново, а сама ссылка при изменении данных не меняется.
Типичная ситуация: разработчик пишет Документ.Контрагент.ИНН и удивляется, почему форма тормозит на 5000 строк. Или бухгалтер переносит данные между базами и получает «пустые» поля вместо контрагентов. Оба случая упираются в то, как устроена ссылка. Разберём механизм работы ссылок в 1С Предприятие: что в ней хранится, какие бывают типы и где подстерегают проблемы.
Что лежит внутри ссылки
Ссылка хранит один уникальный идентификатор (GUID, 16 байт). В таблицах СУБД он лежит в бинарных полях с суффиксом RRef. Никаких кодов, наименований и дат внутри нет.
Из этого следует несколько практических вещей:
- 🔑 Идентификатор присваивается при создании объекта и не меняется до его удаления.
- 🔑 Код, наименование, номер документа и любые реквизиты можно править сколько угодно, ссылка остаётся прежней.
- 🔑 Две ссылки равны, если равны их идентификаторы. Поэтому
Если Ссылка1 = Ссылка2работает быстро и без чтения реквизитов. - 🔑 В разных информационных базах идентификаторы одного и того же «Ромашка ООО» будут разными, если элемент создавали независимо.
Посмотреть идентификатор можно из кода:
Идентификатор = Контрагент.УникальныйИдентификатор();
Сообщить(Строка(Идентификатор));
А построить ссылку по известному идентификатору — так:
Ссылка = Справочники.Контрагенты.ПолучитьСсылку(Идентификатор);
Метод ПолучитьСсылку с идентификатором не проверяет, что объект существует. Вернётся ссылка-«призрак». Проверить можно через Ссылка.ПолучитьОбъект() = Неопределено.
Пустая ссылка и «призрак»
Пустая ссылка — это ссылка с нулевым идентификатором. Она корректна по типу, но ни на что не указывает. Проверяется методом Пустая() или функцией ЗначениеЗаполнено().
Отдельный случай — битая ссылка: идентификатор есть, объекта в базе уже нет. Такое бывает после удаления без контроля или после неаккуратной загрузки данных. Реквизиты по такой ссылке читаются как пустые, а представление показывается как <Объект не найден>.
Объект и ссылка: в чём разница
Это главная путаница у начинающих. Сравним:
| Ссылка | Объект | |
|---|---|---|
| Что это | Адрес записи в базе | Копия данных записи в памяти |
| Тип | СправочникСсылка.Контрагенты | СправочникОбъект.Контрагенты |
| Можно менять реквизиты | Нет | Да |
| Сохраняется в базу | Нет | Через Записать() |
| Доступна на клиенте | Да | Только на сервере |
| Блокировка записи | Не ставит | Ставит при записи |
Переход от одного к другому:
Объект = Ссылка.ПолучитьОбъект();— читает запись целиком в память.- Меняем реквизиты у
Объект. Объект.Записать();— запись уходит в базу.Объект.Ссылка— возвращает ту же ссылку, что и была.
Для нового объекта ссылка появляется ещё до записи. Платформа заранее генерирует идентификатор, и его можно забрать через Объект.ПолучитьСсылкуНового() либо задать вручную через УстановитьСсылкуНового(). Это нужно, когда документ ссылается сам на себя или когда ссылку надо передать в другой объект до сохранения.
Если вам нужно только прочитать пару реквизитов, ПолучитьОбъект() избыточен. Он тянет все реквизиты и табличные части. Обращение через точку или запрос обойдётся дешевле.
Типы ссылок: объектные, перечисления и составные
У каждого ссылочного объекта конфигурации есть свой тип ссылки. Тип привязан к виду и имени объекта, а не к значениям.
Основные ссылочные типы
- 📁
СправочникСсылка.<Имя>— элементы и группы справочника. - 📄
ДокументСсылка.<Имя>— документы. - 🏷️
ПеречислениеСсылка.<Имя>— значения перечислений. Они тоже ссылки, хотя «объекта» у них нет. - 🧮
ПланВидовХарактеристикСсылка,ПланСчетовСсылка,ПланВидовРасчетаСсылка— планы. - 🔄
ПланОбменаСсылка— узлы обмена. - ⚙️
БизнесПроцессСсылка,ЗадачаСсылка— процессы и задачи.
Регистры сведений и накоплений ссылочного типа не имеют: запись регистра ссылкой не адресуется, её находят по набору измерений.
Составной тип
Реквизит может допускать несколько типов сразу, например «Контрагент или Физлицо». В базе для него хранится дополнительное поле с признаком типа. Узнать, какой тип лежит сейчас, можно так:
Если ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") Тогда
// работаем как с контрагентом
КонецЕсли;
Чем шире составной тип, тем тяжелее запросы по нему: платформа вынуждена присоединять несколько таблиц. Для частых отборов лучше держать реквизиты однотипными.
Как узнать, на какой объект указывает ссылка
Метаданные объекта берутся через Ссылка.Метаданные(). Обратная задача, найти вид метаданных по типу, решается через Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)). Это основа универсальных обработок, где объект заранее неизвестен.
Что происходит со ссылкой при изменении данных
Ссылка не копирует данные, поэтому «обновлять» её не нужно. Переименовали контрагента, поправили ИНН, перепровели документ на другую дату: все места, где хранилась ссылка, автоматически видят новое значение. Именно поэтому в 1С можно менять справочник задним числом, и старые документы покажут новое наименование.
Из этого есть исключения, о которых стоит помнить:
- ⏳ В документах и регистрах хранится только ссылка, а не снимок значения. Если нужен «замороженный» на дату вариант (например, наименование на момент отгрузки), его копируют в отдельный реквизит или используют периодический регистр сведений .
- 🗑️ Пометка на удаление не меняет ссылку. Объект остаётся в базе, все ссылки на него живы, пока его физически не удалили.
- 🔁 Физическое удаление (обработка «Удаление помеченных объектов») ломает ссылку, если на объект что-то указывает. Платформа проверяет это и не даёт удалить объект, на который есть ссылки, пока не сработает контроль целостности.
- 🔗 Замена ссылок на другую (при объединении дублей) выполняется специальным механизмом: он находит все места использования и подставляет новое значение. Сам идентификатор у старого объекта при этом не «переезжает».
Перед любым массовым удалением или заменой ссылок сделайте копию базы. Откат такой операции штатными средствами невозможен.
Найти, где используется объект, можно в пользовательском режиме: в списке выделите элемент и вызовите команду поиска мест использования из меню «Ещё» (в разных конфигурациях называется чуть по-разному, найдите её по слову «использования»).
Ссылки в запросах и в коде
Обращение через точку
В запросе конструкция Реализация.Контрагент.ИНН превращается в неявное левое соединение. Платформа сама присоединит таблицу контрагентов. Для двух-трёх полей это нормально, для десятка уже заметно.
В коде на сервере то же обращение Документ.Контрагент.ИНН — это отдельное чтение из базы. Если вызывать его в цикле на тысячу строк, получится тысяча лишних обращений.
Правильнее выбрать всё одним запросом:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Контрагент,
| Контрагенты.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Ссылка В (&Список)";
Запрос.УстановитьПараметр("Список", СписокКонтрагентов);
Выборка = Запрос.Выполнить().Выбрать();
Здесь отбор идёт по полю Ссылка и использует индекс по идентификатору, поэтому быстро.
Сравнение и приведение типов
ГДЕ Контрагент = &Контрагент— прямое сравнение по идентификатору.ГДЕ Контрагент ССЫЛКА Справочник.Контрагенты— проверка, что значение составного реквизита относится к нужному типу.ВЫРАЗИТЬ(Значение КАК Справочник.Контрагенты)— приведение, когда запрос не знает точного типа, а вам нужны реквизиты.ПРЕДСТАВЛЕНИЕ(Ссылка)— человекочитаемая строка вместо ссылки.
Предопределённые элементы
Предопределённый элемент имеет фиксированный идентификатор, заданный в конфигурации. Поэтому на него можно ссылаться и в коде, и в запросе одинаково во всех базах: Справочники.ВидыНоменклатуры.Услуга или Значение(Справочник.ВидыНоменклатуры.Услуга) в тексте запроса. Это единственный надёжный способ «договориться» о ссылке между базами без обмена.
Ссылки на клиенте
На клиенте ссылка передаётся нормально, это просто значение. А вот чтение реквизитов по ней — серверный вызов. Если в обработчике формы на клиенте написать Объект.Контрагент.ИНН, платформа сходит на сервер за каждым таким обращением. В клиентском коде удобнее один раз вызвать серверную функцию и вернуть структуру нужных значений.
Ссылка в отчётах и динамических списках
В отчёте на СКД ссылочное поле выводится как представление, но по нему можно отбирать и группировать именно как по ссылке. Группировка по ссылке схлопывает дубли по идентификатору. Если же сгруппировать по наименованию, два разных контрагента с одинаковым названием сольются в одну строку, а одно и то же лицо с разной записью разойдётся на две. Поэтому в итоговых отчётах всегда группируйте по самой ссылке, а наименование выводите рядом как вспомогательное поле.
В динамическом списке на форме ссылка тоже лежит в основе строки: двойной клик открывает объект именно по ней. Поле «Ссылка» есть у каждой строки, даже если его не выводят в колонки.
Сводная таблица: что чем является
| Понятие | Что хранит | Меняется при правке реквизитов | Где использовать |
|---|---|---|---|
| Ссылка | Идентификатор | Нет | Хранение связей, параметры запросов, передача между клиентом и сервером |
| Объект | Данные записи | Да, в памяти | Изменение и запись данных |
| Менеджер | Правила работы с видом объектов | Не применимо | Справочники.Контрагенты.НайтиПоКоду() |
| Структура/таблица значений | Выбранные значения | Не связаны с базой | Передача результатов, отчёты |
| Предопределённый элемент | Фиксированный идентификатор | Нет | Устойчивые ссылки между базами |
Менеджер удобен для поиска: НайтиПоКоду, НайтиПоНаименованию, НайтиПоРеквизиту. Все они возвращают ссылку, а при неудаче пустую ссылку, а не ошибку.
Проблемы производительности и как их обойти
Типичные места, где ссылки тормозят систему:
- Точка в цикле. Читать реквизиты ссылки внутри цикла по 1000+ записей. Решение: один запрос с соединением либо
ЗначенияРеквизитовОбъекта()для набора ссылок. - Длинные цепочки через точку.
Документ.Договор.Организация.Контрагент.ИНН— три неявных соединения. Лучше выбрать нужный реквизит явнымЛЕВОЕ СОЕДИНЕНИЕ. - Широкие составные типы. Реквизит на 15 типов даёт тяжёлый план запроса. Сузьте тип, если бизнес-логика позволяет.
- Лишний
ПолучитьОбъект(). Для чтения одного поля он тянет весь объект с табличными частями. - Отбор по представлению. Условие по строке вместо отбора по ссылке не использует индекс. Сначала найдите ссылку, потом фильтруйте по ней.
- Пустые ссылки в условиях. Сравнение
= ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)корректно, но проверку «заполнено ли» лучше делать до запроса, а не внутри.
Простой пример с числами. Нужно получить ИНН для 2000 контрагентов. Способ «в цикле через точку» даст порядка 2000 обращений к базе. Один запрос с параметром-списком выполнится одним обращением. На файловой базе разница заметна глазами, на клиент-серверной с сетью между серверами тем более.
Для небольших выборок по одному-двум реквизитам встроенная функция ОбщегоНазначения.ЗначенияРеквизитовОбъекта() из библиотеки стандартных подсистем (БСП) читает значения без загрузки объекта целиком. Если в вашей конфигурации БСП есть, используйте её.
Типичные проблемы со ссылками
Здесь собраны сбои, которые встречаются на практике.
Частые вопросы
Чем ссылка отличается от кода или наименования элемента?
Можно ли хранить ссылку в текстовом поле или файле?
XMLСтрока(Ссылка) даёт строку, а Справочники.Имя.ПолучитьСсылку(Идентификатор) восстанавливает ссылку. Но в другой базе такая строка бесполезна, если объект там не создавался с тем же идентификатором.Почему при выгрузке и загрузке элемент создаётся заново, а не обновляется?
Что будет со ссылкой, если элемент перенести в другую группу?
Родитель, ссылка на сам элемент остаётся прежней.Можно ли вручную поменять идентификатор объекта?
УстановитьСсылкуНового(). Существующий объект «перепрописать» нельзя.Как проверить, существует ли объект по ссылке?
Ссылка.ПолучитьОбъект() <> Неопределено. Для проверок в больших объёмах удобнее запрос по полю Ссылка к таблице объекта.