Тип «Ссылка» во встроенном языке 1С:Предприятия 8.3: из чего состоит, как получить, сравнить и не замедлить программу
Содержание
Ссылка в 1С — это значение, которое указывает на конкретный объект базы данных: элемент справочника, документ, счёт, значение перечисления. Внутри ссылка хранит всего две вещи: тип объекта и его уникальный идентификатор (UUID). Самих данных объекта в ней нет — за ними платформа идёт в базу, когда вы их запрашиваете.
Отсюда практические правила работы со ссылками в коде. Ссылку дёшево передавать между клиентом и сервером, хранить в реквизитах и коллекциях, сравнивать через =. А вот каждое обращение «через точку» к её реквизитам — это чтение из базы. Ниже разобраны виды ссылок, способы их получения, сравнение, преобразование в строку и ошибки, на которых чаще всего теряют производительность.
Из чего состоит ссылка и чем она отличается от объекта
Возьмём реквизит документа Контрагент. В таблице документа хранится не наименование и не ИНН контрагента, а 16 байт его идентификатора. Платформа знает, к какой таблице относится этот идентификатор, по типу реквизита. Если тип составной, рядом хранится ещё и номер таблицы.
Поэтому ссылка:
- 🔑 неизменяема — нельзя «поменять» ссылку, можно только присвоить переменной другую;
- 📦 лёгкая — передаётся на клиент и обратно без сериализации всего объекта;
- 🔍 уникальна в пределах базы — два разных объекта не могут иметь одинаковую ссылку;
- 🔒 только для чтения — изменить данные через ссылку нельзя, для записи нужен объект.
Сравните три близких по смыслу, но разных типа данных:
| Тип | Пример | Что содержит | Можно ли записать |
|---|---|---|---|
| Ссылка | СправочникСсылка.Контрагенты | Тип + UUID | Нет |
| Объект | СправочникОбъект.Контрагенты | Все реквизиты и табличные части в памяти | Да, методом Записать() |
| Выборка | СправочникВыборка.Контрагенты | Текущую запись при обходе | Нет, но можно получить объект |
Переход от ссылки к объекту выполняется методом ПолучитьОбъект(). Обратно — через свойство Ссылка объекта:
// На сервере
Объект = Ссылка.ПолучитьОбъект(); // читаем объект целиком
Объект.Комментарий = "Проверено";
Объект.Записать();
НоваяСсылка = Объект.Ссылка; // ссылка записанного объекта
У нового, ещё не записанного объекта свойство Ссылка содержит пустую ссылку. Настоящая ссылка появляется после первой записи. Если ссылка нужна раньше (например, чтобы связать два новых объекта), используйте УстановитьСсылкуНового() или ПолучитьСсылкуНового().
Какие бывают ссылочные типы в 1С:Предприятии
Ссылочный тип есть у каждого объекта конфигурации, который хранится в базе как отдельная запись со своим идентификатором. Регистры к ним не относятся: у записи регистра ссылки нет, её определяет набор измерений и период.
| Объект конфигурации | Ссылочный тип | Пример получения |
|---|---|---|
| Справочник | СправочникСсылка.<Имя> | Справочники.Валюты.НайтиПоКоду("643") |
| Документ | ДокументСсылка.<Имя> | Документы.РеализацияТоваровУслуг.НайтиПоНомеру(...) |
| Перечисление | ПеречислениеСсылка.<Имя> | Перечисления.ВидыОпераций... |
| План счетов | ПланСчетовСсылка.<Имя> | ПланыСчетов.Хозрасчетный.НайтиПоКоду("60.01") |
| План видов характеристик | ПланВидовХарактеристикСсылка.<Имя> | ПланыВидовХарактеристик.<Имя>.НайтиПоНаименованию(...) |
| План видов расчёта | ПланВидовРасчетаСсылка.<Имя> | ПланыВидовРасчета.<Имя>.НайтиПоКоду(...) |
| План обмена | ПланОбменаСсылка.<Имя> | ПланыОбмена.<Имя>.ЭтотУзел() |
| Бизнес-процесс | БизнесПроцессСсылка.<Имя> | через запрос или выборку |
| Задача | ЗадачаСсылка.<Имя> | через запрос или выборку |
| Точка маршрута | ТочкаМаршрутаБизнесПроцессаСсылка.<Имя> | БизнесПроцессы.<Имя>.ТочкиМаршрута.<Точка> |
Особняком стоят перечисления. Их значения задаются в конфигураторе, а не создаются пользователем. В коде к ним обращаются по имени: Перечисления.СтавкиНДС.НДС20. Записать значение перечисления как объект нельзя — у него нет метода ПолучитьОбъект().
Предопределённые элементы
Справочники, планы счетов и планы видов характеристик могут иметь предопределённые элементы. На них ссылаются по имени, а не по коду, — так код не ломается, если пользователь поменял код или наименование:
Счет = ПланыСчетов.Хозрасчетный.ОсновныеСредства;
На клиенте и в общих модулях, где менеджеры недоступны, используется функция ПредопределенноеЗначение():
Счет = ПредопределенноеЗначение("ПланСчетов.Хозрасчетный.ОсновныеСредства");
Ставка = ПредопределенноеЗначение("Перечисление.СтавкиНДС.НДС20");
Как получить ссылку в коде
Способов несколько. Выбор зависит от того, что вы знаете об объекте заранее.
Через методы менеджера
Менеджер объекта (Справочники.<Имя>, Документы.<Имя>) умеет искать:
- 🔢
НайтиПоКоду(Код)— по коду справочника или плана счетов; - 🏷️
НайтиПоНаименованию(Наименование, ТочноеСоответствие)— по наименованию; - 🧾
НайтиПоРеквизиту(ИмяРеквизита, Значение)— по значению реквизита; - 📄
НайтиПоНомеру(Номер, ДатаИнтервала)— для документов.
Если ничего не найдено, метод возвращает пустую ссылку этого типа, а не Неопределено. Проверять результат нужно так:
Валюта = Справочники.Валюты.НайтиПоКоду("840");
Если Валюта.Пустая() Тогда
ВызватьИсключение "Валюта с кодом 840 не найдена";
КонецЕсли;
НайтиПоНаименованию и НайтиПоРеквизиту возвращают первый найденный элемент. Если наименования повторяются (а в справочнике контрагентов так бывает часто), вы получите случайный из дублей. Для таких поисков надёжнее запрос с проверкой количества строк.
Через запрос
Запрос — основной способ получить ссылки по условию. Поле Ссылка есть у таблицы любого ссылочного объекта:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН = &ИНН
| И НЕ Контрагенты.ПометкаУдаления";
Запрос.УстановитьПараметр("ИНН", "7700000000");
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Контрагент = Выборка.Ссылка;
КонецЕсли;
По уникальному идентификатору
Если UUID известен (пришёл из обмена, из внешней системы, из навигационной ссылки), ссылку собирают методом ПолучитьСсылку():
УИД = Новый УникальныйИдентификатор("4f2a1c3e-8b7d-11ee-a1b2-0050569f1a2b");
Ссылка = Справочники.Номенклатура.ПолучитьСсылку(УИД);
ПолучитьСсылку() не проверяет, существует ли объект в базе. Он просто собирает ссылку из типа и идентификатора. Чтобы убедиться, что объект есть, обратитесь к нему запросом или проверьте Ссылка.ПолучитьОбъект() <> Неопределено.
Пустая ссылка
У каждого ссылочного типа есть одно «пустое» значение. Его получают через менеджер или в запросе через ЗНАЧЕНИЕ():
Пустая = Справочники.Номенклатура.ПустаяСсылка();
ГДЕ Товары.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
Сравнение ссылок и поиск в коллекциях
Ссылки сравниваются по значению. Две переменные равны, если у них совпадают тип и UUID. При этом неважно, как именно каждая ссылка была получена — из запроса, из реквизита или методом НайтиПоКоду().
Если Документ.Организация = ОсновнаяОрганизация Тогда
// это одна и та же запись справочника
КонецЕсли;
Что нужно помнить при сравнении:
- ⚖️ Ссылки разных типов никогда не равны, даже при одинаковом UUID.
- 🚫 Операции
<и>для ссылок не работают — платформа выдаст ошибку сравнения. Для упорядочивания сортируйте по реквизиту (наименованию, дате, коду). - 🕳️ Пустая ссылка не равна
Неопределено. Реквизит составного типа может содержать и то и другое. - ✅ Для проверки «заполнено ли» используйте
ЗначениеЗаполнено(Значение). Она вернётЛожьи для пустой ссылки, и дляНеопределено.
| Проверка | Пустая ссылка | Неопределено | Заполненная ссылка |
|---|---|---|---|
Ссылка.Пустая() | Истина | ошибка: у значения нет метода | Ложь |
Значение = Неопределено | Ложь | Истина | Ложь |
ЗначениеЗаполнено(Значение) | Ложь | Ложь | Истина |
Ссылки в массивах, соответствиях и таблицах
Раз ссылки сравниваются по значению, их удобно использовать в коллекциях:
// Проверка вхождения
Если МассивОрганизаций.Найти(Организация) <> Неопределено Тогда
// организация уже есть в списке
КонецЕсли;
// Ссылка как ключ соответствия: накапливаем суммы по контрагентам
Итоги = Новый Соответствие;
Для Каждого Строка Из ТаблицаОплат Цикл
Текущая = Итоги.Получить(Строка.Контрагент);
Итоги.Вставить(Строка.Контрагент, ?(Текущая = Неопределено, 0, Текущая) + Строка.Сумма);
КонецЦикла;
В таблице значений поиск по ссылке выполняют методами Найти() и НайтиСтроки(). Если таблица большая и поиск идёт в цикле, добавьте индекс по колонке: Таблица.Индексы.Добавить("Контрагент").
Массив ссылок можно передать параметром в запрос и использовать в условии В (&Массив). Это быстрее, чем выполнять запрос по каждой ссылке отдельно.
Разыменование: обращение к реквизитам через точку
Запись Документ.Контрагент.ИНН выглядит невинно. Но на сервере платформа при этом читает данные контрагента из базы. Внутри одного вызова прочитанное кешируется, однако в цикле по тысяче документов с разными контрагентами вы получите тысячу обращений к базе.
Правила, которые снимают проблему:
- Нужные реквизиты связанных объектов получайте в том же запросе, что и основные данные:
Реализация.Контрагент.ИНН КАК ИНН. - Если нужен один-два реквизита одной ссылки, используйте функции БСП
ОбщегоНазначения.ЗначениеРеквизитаОбъекта()иОбщегоНазначения.ЗначенияРеквизитовОбъекта()— они читают только указанные поля одним запросом. - Не вызывайте
ПолучитьОбъект()ради чтения. Объект загружает все реквизиты и табличные части — это заметно тяжелее.
На тонком и веб-клиенте разыменование ссылок через точку недоступно вовсе. На клиенте ссылка — это только тип и идентификатор. Реквизиты получают серверным вызовом без контекста формы (&НаСервереБезКонтекста) или заранее кладут в реквизиты формы.
Разыменование в запросах и составные типы
В языке запросов точка тоже порождает соединение с таблицей. Если поле составного типа (например, Регистратор или Документ-основание), платформа соединит его со всеми возможными таблицами. Для регистра с десятком регистраторов это десяток соединений.
Решение — явно привести тип через ВЫРАЗИТЬ:
ВЫБРАТЬ
ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг).Номер КАК Номер
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Оператор ССЫЛКА проверяет, относится ли значение составного поля к нужному типу. В коде на встроенном языке ту же задачу решает ТипЗнч(Значение) = Тип("ДокументСсылка.РеализацияТоваровУслуг").
Преобразование ссылки в строку и обратно
Ссылку часто нужно сохранить вне базы: передать в другую систему, записать в файл, вставить в письмо. Здесь легко ошибиться, потому что способов превратить ссылку в строку несколько, и они дают разный результат.
| Функция | Что вернёт | Можно ли восстановить ссылку |
|---|---|---|
Строка(Ссылка) | Представление: наименование или «Реализация 00-000123 от 15.03…» | Нет |
XMLСтрока(Ссылка) | UUID в виде 4f2a1c3e-8b7d-... | Да, если тип известен |
ЗначениеВСтрокуВнутр(Ссылка) | Внутренний формат с типом и UUID | Да, ЗначениеИзСтрокиВнутр() |
Ссылка.УникальныйИдентификатор() | Значение типа УникальныйИдентификатор | Да, через ПолучитьСсылку() |
ПолучитьНавигационнуюСсылку(Ссылка) | Строка вида e1cib/data/Справочник.Номенклатура?ref=... | Да, через переход по ссылке в клиенте |
Восстановление из UUID при известном типе:
СтрокаУИД = XMLСтрока(Номенклатура); // сохранили
// ... позже, в другом месте
Номенклатура = XMLЗначение(Тип("СправочникСсылка.Номенклатура"), СтрокаУИД);
Не храните ссылки как результат Строка(). Наименования повторяются и меняются, а представление документа зависит от номера и даты. Через полгода найти по такой строке нужный объект не получится.
ЗначениеВСтрокуВнутр() подходит для обмена между базами одной конфигурации — например, для временного хранения в регистре сведений. Для обмена с внешними системами надёжнее пара «имя типа + UUID»: формат внутренней строки — служебный, и разбирать его вручную не стоит.
Навигационная ссылка удобна пользователю. Скопировать её можно из открытой формы объекта командой «Получить ссылку» (сочетание Shift+F11), а открыть — командой «Перейти по ссылке» из главного меню.
Битые ссылки и ссылочная целостность
Битая ссылка — значение, которое указывает на запись, которой в базе уже нет. В форме такое значение отображается как <Объект не найден> (…) с идентификатором в скобках. Откуда оно берётся:
- 🗑️ объект удалили напрямую, без контроля ссылочной целостности;
- 🔄 при обмене пришла ссылка на объект, который ещё не загружен;
- 🧩 ссылку собрали через
ПолучитьСсылку()из UUID, которого нет в базе; - 💾 база восстановлена частично, или таблицы правили внешними средствами.
В коде битую ссылку выдаёт ПолучитьОбъект(): для несуществующего объекта он возвращает Неопределено. Проверка Пустая() при этом вернёт Ложь — ведь UUID заполнен.
Объект = Ссылка.ПолучитьОбъект();
Если Объект = Неопределено Тогда
// ссылка битая: объекта в базе нет
КонецЕсли;
Найти все битые ссылки в базе помогает режим тестирования и исправления в конфигураторе (Администрирование → Тестирование и исправление) с проверкой логической и ссылочной целостности. Перед запуском нужна резервная копия и монопольный доступ к базе — пользователи должны выйти. Выберите режим «Создавать объекты» или «Очищать ссылки» осознанно: первый создаёт пустые элементы-заглушки, второй стирает значения в реквизитах.
Чтобы битые ссылки не появлялись, удаляйте объекты через пометку удаления и обработку «Удаление помеченных объектов». Она проверяет, не используется ли объект где-то ещё, и не даст удалить то, на что ссылаются документы.
Где чаще всего ошибаются
Ещё несколько ошибок, которые не дают сообщений, но портят результат:
- 🔁 Сравнение с
Неопределеновместо проверки заполненности. Пустая ссылка проходит такую проверку как «заполненная». - 🧮 Ссылки как строки в ключах.
Соответствиес ключомСтрока(Ссылка)склеит разных контрагентов с одинаковым наименованием. - 🧷 Кеширование объектов вместо ссылок. Объект в переменной модуля или в параметрах сеанса быстро устаревает, а ссылка — нет: данные по ней всегда читаются актуальные.