Проверка ссылок на объекты в 1С: существует, пуста или битая
Содержание
Ссылку в 1С проверяют тремя способами: смотрят, заполнена ли она (ЗначениеЗаполнено), читают объект по ссылке (ПолучитьОбъект() вернёт Неопределено, если записи нет) или выбирают запись запросом. Первый способ дешёвый, но ничего не знает о базе. Второй точный, но тяжёлый. Третий лучше всего подходит для массовых проверок.
Под «проверить ссылку на объект» разработчики понимают сразу несколько задач: убедиться, что ссылка не пустая; узнать, существует ли объект; найти, кто на него ссылается; отыскать «битые» ссылки в данных. Все четыре разобраны ниже, с кодом для платформы 8.3 и запросами, которые можно вставить в консоль.
Что считать «плохой» ссылкой
Ссылка в 1С хранит тип и уникальный идентификатор. Саму запись она не содержит. Поэтому ссылка бывает в трёх состояниях, и путать их нельзя:
- ⚪ Пустая — идентификатор нулевой, например
Справочники.Контрагенты.ПустаяСсылка(). Это нормальное значение, оно означает «ничего не выбрано». - ✅ Живая — по ней есть запись в таблице базы данных.
- ❌ Битая — идентификатор не нулевой, а записи нет. В интерфейсе такое значение показывается как
<Объект не найден>с названием типа.
Битая ссылка появляется после аварийного обновления, загрузки данных из другой базы без связанных объектов, удаления в обход контроля или ручной правки таблиц. Платформа в этом случае не падает: поле просто отображает заглушку, а код получает значение, с которым нельзя работать.
ЗначениеЗаполнено(Ссылка) вернёт Истина для битой ссылки. Функция проверяет только то, что ссылка не пустая.
Проверить, что ссылка заполнена и нужного типа
Начинать стоит с самого дешёвого. Это единственная проверка, которая не обращается к базе.
Если Не ЗначениеЗаполнено(Контрагент) Тогда
Возврат;
КонецЕсли;
Если ТипЗнч(Контрагент) <> Тип("СправочникСсылка.Контрагенты") Тогда
ВызватьИсключение "Ожидался контрагент, получено: " + Строка(ТипЗнч(Контрагент));
КонецЕсли;
У самой ссылки есть метод Пустая(), он работает так же, как проверка на пустую ссылку своего типа. Для реквизитов составного типа сначала проверяйте тип через ТипЗнч, потом заполненность: пустая ссылка одного типа не равна пустой ссылке другого.
💡 Предопределённый элемент сравнивайте с Справочники.Имя.ИмяЭлемента, а не с наименованием. Наименование пользователь поменяет, ссылка останется прежней.
Существует ли объект по ссылке
Чтобы убедиться, что запись есть, выберите один раз по ней данные. Три рабочих способа:
| Способ | Что делает | Когда выбирать |
|---|---|---|
Ссылка.ПолучитьОбъект() | Читает объект целиком, при отсутствии возвращает Неопределено | Нужно сразу менять объект |
Запрос ВЫБРАТЬ ПЕРВЫЕ 1 | Читает одну строку, объект не создаётся | Только проверка, особенно в цикле |
Чтение реквизита через ОбщегоНазначения.ЗначениеРеквизитаОбъекта (БСП) | Возвращает значение поля, для несуществующей ссылки — пустое | Нужен реквизит, а не факт существования |
Минимальный вариант с запросом:
Функция ОбъектСуществует(Ссылка)
Если Не ЗначениеЗаполнено(Ссылка) Тогда
Возврат Ложь;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ИСТИНА КАК Есть
|ИЗ
| " + Ссылка.Метаданные().ПолноеИмя() + " КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Возврат Не Запрос.Выполнить().Пустой();
КонецФункции
Имя таблицы подставляется из метаданных ссылки, поэтому функция годится для любого ссылочного типа: справочника, документа, плана видов характеристик. Для составного реквизита типов может быть несколько, но функция работает с конкретным значением, так что это не мешает.
Тонкость с ПолучитьСсылку
Метод менеджера Справочники.Имя.ПолучитьСсылку(УникальныйИдентификатор) строит ссылку по идентификатору и не проверяет, есть ли такая запись. Если вы разбираете данные из обмена или из внешнего файла, после этого вызова ссылка может оказаться битой. Следом проверяйте её описанным выше запросом.
Метод ПолучитьОбъект() на ссылке, по которой записи нет, не выбрасывает исключение, а возвращает Неопределено. Поэтому проверка результата обязательна: без неё следующая строка с обращением к реквизиту упадёт с ошибкой «Поле объекта не обнаружено».
Кто ссылается на объект
Обратная задача: перед удалением или переносом узнать, где используется элемент. В режиме 1С:Предприятие это делает пункт «Где используется» в командах списка и формы (в современных конфигурациях он доступен из меню «Ещё» или контекстного меню). Из кода — метод НайтиПоСсылкам.
Ссылки = Новый Массив;
Ссылки.Добавить(Контрагент);
Результат = НайтиПоСсылкам(Ссылки);
Для Каждого Строка Из Результат Цикл
Сообщить(Строка.Метаданные.ПолноеИмя() + " : " + Строка.Данные);
КонецЦикла;
В таблице результата три колонки: Ссылка (что искали), Данные (где нашли) и Метаданные (описание объекта, в котором нашли). Если таблица пустая, использований нет.
Две оговорки:
- Поиск проходит по всей базе и на большой базе занимает минуты. Не вызывайте его в обработчиках проведения и в циклах.
- Он видит и помеченные на удаление объекты. Поэтому «используется только в помеченных» — отдельный случай, и разбирает его стандартная обработка «Поиск и удаление помеченных объектов» (раздел «Администрирование», пункт для работы с удалением помеченных объектов).
Консоль запросов: найти битые ссылки массово
Один документ проверить просто. Сложнее найти все записи, где ссылка уже не указывает на живой объект. Здесь помогает консоль запросов (внешняя обработка, её распространяют на ИТС и в профильных сообществах) и левое соединение.
Допустим, нужно найти документы поступления, у которых контрагент не существует:
ВЫБРАТЬ
Док.Ссылка КАК Документ,
Док.Контрагент КАК БитаяСсылка
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Док
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Док.Контрагент = Контрагенты.Ссылка
ГДЕ
Док.Контрагент <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
И Контрагенты.Ссылка ЕСТЬ NULL
Логика простая. Если в левом соединении справа ничего не нашлось, поле Ссылка будет NULL. Условие на пустую ссылку отсекает документы, где контрагент просто не выбран.
Тот же приём работает для регистров: соедините измерение с таблицей справочника и ищите ЕСТЬ NULL. На больших регистрах добавляйте отбор по периоду, иначе запрос будет читать всё.
💡 Консоль запросов всё равно работает в пользовательском режиме, права и RLS действуют. Если запрос возвращает меньше, чем ожидали, проверьте, под каким пользователем вы вошли: для диагностики нужен полноправный.
Структура базы: что лежит под ссылкой
Иногда причину не понять, пока не посмотрите, как объект хранится физически. Сопоставление объектов метаданных и таблиц СУБД даёт функция ПолучитьСтруктуруХраненияБазыДанных():
Структура = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого Стр Из Структура Цикл
Если Найти(Стр.Метаданные, "Справочник.Контрагенты") > 0 Тогда
Сообщить(Стр.ИмяТаблицы + " -> " + Стр.ИмяТаблицыХранения);
КонецЕсли;
КонецЦикла;
Вызов доступен администратору. Он возвращает таблицу значений, где видно, что справочнику Контрагенты соответствует таблица вида _Reference<номер> в базе. Это помогает, когда нужно сравнить количество записей в SQL с тем, что видит запрос 1С, или понять, чьи строки табличной части ссылаются на элемент.
Практический порядок диагностики, когда пользователь жалуется на <Объект не найден> в документе:
- Откройте документ и найдите поле, где значение не отображается. Запомните имя реквизита.
- Выполните запрос с левым соединением по этому реквизиту и посчитайте, сколько документов затронуто. Один случай — ручная правка или сбой, сотни — проблема обмена или загрузки.
- Определите период: отсортируйте найденные документы по дате. Если все они попали в базу в один день, ищите событие в этот день — обновление, загрузку, запуск обмена.
- Решите, что делать со значением: восстановить объект из резервной копии, заполнить вручную или очистить реквизит.
Читать таблицы напрямую можно. Править их нельзя: платформа хранит служебные поля и индексы, а ручная правка ломает целостность сильнее, чем исходная проблема. Исправляйте данные средствами 1С.
Типичные причины битых ссылок и как их ловить
Битая ссылка — симптом. Откуда она берётся, зависит от сценария:
- 🔁 Обмен данными. Документ приехал, а справочник, на который он ссылается, не попал в выгрузку из-за отбора в правилах обмена.
- 📦 Загрузка
.dtи ручные загрузки. Данные грузили частями или в базу с другой конфигурацией. - 🗑 Удаление в обход контроля. Элемент удалён программно, без проверки ссылок (
Объект.Удалить()из обработки без вызова поиска использований). - 💥 Сбой. Аварийное завершение при записи, повреждение файла базы.
- 🧩 Внешние обработки. Код, который собирает ссылки из строк и идентификаторов без проверки.
Профилактика сводится к четырём привычкам: перед удалением вызывать НайтиПоСсылкам; после ПолучитьСсылку проверять существование; в правилах обмена включать объекты-зависимости; после крупной загрузки прогонять тестирование базы
(оно описано ниже).
Ссылочная целостность: тестирование и проверка конфигурации
В Конфигураторе есть два встроенных инструмента, и они проверяют разное.
Тестирование и исправление информационной базы — Администрирование → Тестирование и исправление. Здесь отмечают проверку логической и ссылочной целостности и выбирают режим: «Проверить только» или проверка с исправлением. При исправлении можно задать, что делать с битыми ссылками: очищать их, создавать недостающие объекты или удалять объекты с нарушенной структурой. Выбор зависит от того, что для вас ценнее — сохранить строки с очищенной ссылкой или получить заглушки.
Условия запуска:
- Работа в монопольном режиме — пользователи из базы должны выйти.
- Резервная копия сделана заранее. Режим исправления меняет данные.
- Сначала запуск «только проверить», разбор отчёта, затем решение об исправлении.
Проверка конфигурации — Конфигурация → Проверка конфигурации. Она работает не с данными, а со схемой: ищет ссылки на удалённые объекты метаданных, ошибки в формах, синтаксические ошибки модулей. Флажок поиска ссылок на удалённые объекты полезен после ручного удаления реквизитов и подсистем.
При обновлении конфигурации
Перед обновлением типовой конфигурации стоит прогнать обе проверки: при реструктуризации платформа пересоздаёт таблицы, и повреждённые данные чаще всего всплывают именно в этот момент. Если в базе есть доработанные расширения, проверьте расширения отдельно: ссылка на удалённый в новой версии объект типовой конфигурации ломает расширение, а не данные.
Что обновление делает с данными, зависит от того, что изменилось в метаданных:
| Изменение в конфигурации | Что будет с данными |
|---|---|
| Реквизит удалён | Значения в таблице пропадают |
| Тип реквизита изменён | Значения, не подходящие под новый тип, обнуляются |
| Справочник удалён из конфигурации | Записи пропадают, ссылки на них в других объектах становятся битыми |
Последняя строка таблицы и есть основной источник проблем, поэтому её нужно проверять запросом из раздела выше до, а не после обновления.
Внешние источники данных: ссылок нет, а проверка нужна
Когда 1С читает таблицы другой СУБД через внешний источник данных, ссылочная модель платформы перестаёт быть гарантией. Ссылки на строки таблиц внешнего источника платформа строит по ключу, но следить за их жизнью не может: внешнюю базу меняют независимо.
Что это значит на практике:
- Контроль целостности остаётся на стороне внешней СУБД (внешние ключи) и вашего кода. Платформа не поймёт, что строку удалили.
- Хранить ссылку на внешнюю строку в собственном справочнике рискованно. Надёжнее хранить значение ключа и при чтении проверять, что запись ещё есть.
- Проверка делается обычным запросом к таблице внешнего источника с отбором по ключу, как в разделе про существование объекта. Для массовой проверки используйте левое соединение внутри одного источника.
- Таблицы без уникального ключа ссылок не дают вообще, и задача проверки сводится к сравнению значений полей.
Прежде чем отправлять запросы в рабочую внешнюю базу, согласуйте нагрузку с её владельцем: большое соединение без отбора может заметно нагрузить чужой сервер.
Частые вопросы
Чем `ЗначениеЗаполнено` отличается от проверки существования?
ЗначениеЗаполнено проверяет, что ссылка не пустая. Существование объекта она не проверяет: для битой ссылки вернёт Истина.Что вернёт `ПолучитьОбъект()` для несуществующей ссылки?
Неопределено. Исключения нет, поэтому результат нужно проверять до обращения к реквизитам.Как увидеть, что в поле лежит битая ссылка?
<Объект не найден> с названием типа. В отладчике то же значение видно в строковом представлении.Можно ли найти все битые ссылки одним действием?
ЕСТЬ NULL.