«Объект не найден» в данных: поиск битых ссылок запросом и их устранение

· 10 мин

Содержание

Битую ссылку в запросе 1С находят левым соединением с таблицей объекта: если реквизит заполнен, а поле Ссылка присоединённой таблицы вернулось NULL, значит, объекта в базе нет. На экране такое значение выглядит как <Объект не найден> (45:9f3a0c1d…).

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

Что такое битая ссылка и почему её не видно в конструкторе запроса

Ссылочный реквизит хранит не наименование, а внутренний идентификатор объекта (GUID). Пока объект с этим GUID существует, платформа подставляет его представление. Когда объекта нет, в поле остаётся «осиротевший» идентификатор.

Сам конструктор запроса о битых ссылках ничего не знает. Он работает с метаданными, а не с данными. Поэтому запрос составляется и выполняется без ошибок, а «сломанные» значения просто приходят в результат.

Путать с битой ссылкой не стоит две другие ошибки, которые выдаёт текст запроса:

  • 🔸 Поле не найдено "Док.СтарыйРеквизит" — в тексте упоминается реквизит, которого больше нет в конфигурации;
  • 🔸 Таблица не найдена "Справочник.Удалённый" — объект метаданных переименовали или удалили.

Это ошибки синтаксиса: запрос не выполнится вообще, а конструктор при открытии такого текста покажет сообщение с номером строки и колонки. Лечатся правкой текста запроса. Битая ссылка — проблема данных, и запрос с ней отрабатывает «успешно».

Откуда берутся битые ссылки

ПричинаКак выглядит
Объект удалили непосредственно, без контроля ссылокВо всех документах, где он был, <Объект не найден>
Обмен прислал документ раньше справочникаБитые ссылки исчезают после следующего сеанса обмена
Загрузка из XML или другой базы с неполным набором данныхБитые значения только в загруженных объектах
Из конфигурации удалили значение перечисленияСтарые документы ссылаются на несуществующее значение
Частичное восстановление таблиц или сбой файловой базыБитые ссылки разбросаны хаотично

Удаление помеченных объектов штатной обработкой проверяет ссылки и не удаляет используемые. Поэтому чаще всего виноваты обработки и код, которые вызывают Удалить() у объекта напрямую.

Запрос, который возвращает битые ссылки

Базовый шаблон — левое соединение с таблицей того типа, на который ссылается реквизит. Пример для контрагента в реализациях:

ВЫБРАТЬ
    Док.Ссылка КАК Документ,
    Док.Контрагент КАК БитоеЗначение
ИЗ
    Документ.РеализацияТоваровУслуг КАК Док
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Спр
        ПО Док.Контрагент = Спр.Ссылка
ГДЕ
    Док.Контрагент <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
    И Спр.Ссылка ЕСТЬ NULL

Условие на пустую ссылку обязательно. Пустое значение тоже не найдёт пары в справочнике, и без этого фильтра в результат попадут все незаполненные документы.

Реквизит составного типа

Если реквизит может хранить ссылки разных типов (например, Договор или Документ-основание), добавьте проверку типа через ССЫЛКА. Иначе любое значение другого типа будет выглядеть как битое:

ВЫБРАТЬ
    Док.Ссылка КАК Документ,
    Док.ДокументОснование КАК БитоеЗначение
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Док
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПоставщику КАК Осн
        ПО Док.ДокументОснование = Осн.Ссылка
ГДЕ
    Док.ДокументОснование ССЫЛКА Документ.ЗаказПоставщику
    И Осн.Ссылка ЕСТЬ NULL

Для каждого типа из состава реквизита нужен свой запрос или свой блок ОБЪЕДИНИТЬ ВСЕ.

Табличные части и регистры

Та же схема работает для табличной части (Документ.РеализацияТоваровУслуг.Товары, поле Номенклатура) и для регистров (РегистрНакопления.ТоварыНаСкладах, измерение Склад). В регистре битое измерение особенно опасно: остатки по нему не закроются ни одним документом.

Для быстрой ручной проверки можно обратиться к реквизиту через точку: Док.Контрагент.ПометкаУдаления ЕСТЬ NULL. У существующего объекта пометка удаления никогда не бывает NULL, так что условие отберёт только битые значения. Явное соединение читается проще и работает предсказуемее, его и берите в обработку.

Как диагностировать ошибку через сообщения и журнал регистрации

Битая ссылка редко сообщает о себе прямо. Обычно пользователь видит следствие: странную строку в отчёте или ошибку при записи. Задача — от симптома дойти до конкретного документа и реквизита.

Окно сообщений

Если обработка падает на объекте с битой ссылкой, выведите контекст до места ошибки. В цикле по выборке это одна строка:

Сообщить(СтрШаблон("Документ: %1; значение: %2; GUID: %3",
    Выборка.Документ, Выборка.БитоеЗначение,
    Выборка.БитоеЗначение.УникальныйИдентификатор()));

УникальныйИдентификатор() работает и у битой ссылки: GUID в ней сохранился, пропал только сам объект. По этому GUID объект потом ищут в резервной копии или в базе-источнике обмена.

Журнал регистрации

В типовых конфигурациях на БСП журнал открывается через Администрирование → Обслуживание → Журнал регистрации, в конфигураторе — Администрирование → Журнал регистрации.

  1. Установите отбор по периоду, когда впервые заметили проблему.
  2. Отберите события удаления данных (Данные. Удаление): они пишутся с уровнем «Информация», поэтому отбор только по ошибкам их скроет.
  3. В колонке «Данные» будет удалённый объект, в колонке «Пользователь» — кто или что его удалило.
  4. Если удаление сделал фоновый сеанс, ищите в соседних событиях имя регламентного задания или обработки.

Журнал хранит события только за настроенный срок. Если удаление было давно, записи могли уже очиститься. Тогда остаётся сравнение с резервной копией.

Диагностика: выберите симптом

Поиск по всей базе: ОбъектМетаданных и динамический текст запроса

Писать запрос руками под каждый реквизит долго. Если нужно проверить все документы, текст собирают программно, обходя метаданные. Ключевой метод — Метаданные.НайтиПоТипу(): по типу ссылки он возвращает объект метаданных, а ПолноеИмя() даёт имя таблицы для запроса.

Процедура НайтиБитыеСсылкиВДокументах()

    Для Каждого МетаДок Из Метаданные.Документы Цикл
        Для Каждого Рекв Из МетаДок.Реквизиты Цикл
            Для Каждого ТипРекв Из Рекв.Тип.Типы() Цикл

                МетаТип = Метаданные.НайтиПоТипу(ТипРекв);
                Если МетаТип = Неопределено Тогда
                    Продолжить; // примитивный тип: строка, число, дата
                КонецЕсли;

                ИмяТаблицы = МетаТип.ПолноеИмя();
                ТекстЗапроса = СтрШаблон(
                    "ВЫБРАТЬ
                    |    Т.Ссылка КАК Документ,
                    |    Т.%2 КАК БитоеЗначение
                    |ИЗ
                    |    %1 КАК Т
                    |        ЛЕВОЕ СОЕДИНЕНИЕ %3 КАК Спр
                    |        ПО Т.%2 = Спр.Ссылка
                    |ГДЕ
                    |    Т.%2 ССЫЛКА %3
                    |    И Т.%2 <> ЗНАЧЕНИЕ(%3.ПустаяСсылка)
                    |    И Спр.Ссылка ЕСТЬ NULL",
                    МетаДок.ПолноеИмя(), Рекв.Имя, ИмяТаблицы);

                Запрос = Новый Запрос(ТекстЗапроса);
                Выборка = Запрос.Выполнить().Выбрать();
                Пока Выборка.Следующий() Цикл
                    Сообщить(СтрШаблон("%1 | %2 | %3 | %4",
                        Выборка.Документ, Рекв.Имя, ИмяТаблицы,
                        Выборка.БитоеЗначение.УникальныйИдентификатор()));
                КонецЦикла;

            КонецЦикла;
        КонецЦикла;
    КонецЦикла;

КонецПроцедуры

Что важно в этом коде:

  • 🔹 НайтиПоТипу() для строки, числа и даты возвращает Неопределено, поэтому примитивные типы пропускаются одной проверкой;
  • 🔹 условие ССЫЛКА делает запрос корректным и для составных реквизитов: каждый тип проверяется отдельным проходом;
  • 🔹 запросов получится много — по одному на пару «реквизит — тип». На большой базе запускайте обход вне рабочего времени или ограничьте его списком документов.

Чтобы проверить табличные части, добавьте вложенный цикл по МетаДок.ТабличныеЧасти и их Реквизиты. Имя таблицы тогда — МетаДок.ПолноеИмя() + "." + ТЧ.Имя. Остальной текст запроса менять не нужно: поле Ссылка в таблице табличной части есть и указывает на документ-владелец.

Как проверить одну ссылку в коде

Для точечной проверки значения не нужен обход. Если в конфигурации есть БСП, используйте ОбщегоНазначения.СсылкаСуществует(Ссылка). Без БСП подойдёт короткий запрос:

Функция СсылкаЖивая(Ссылка)
    ИмяТаблицы = Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)).ПолноеИмя();
    Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1 1 ИЗ " + ИмяТаблицы + " ГДЕ Ссылка = &Ссылка");
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции

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

Почему битые ссылки появляются после обновления конфигурации

Обновление само по себе ссылки не ломает. Платформа при реструктуризации переносит данные, а значения, тип которых стал недопустим, очищает. Битые ссылки появляются в других сценариях.

  • ⚙️ Удалили значение перечисления. Документы прошлых лет продолжают хранить ссылку на него. Перед удалением значения из перечисления в своей доработке сначала замените его в данных.
  • ⚙️ Обработка перехода удалила «лишние» объекты. Если в доработанной конфигурации свой код ссылался на них, ссылки остались висеть.
  • ⚙️ Сняли объект с поддержки и удалили, а данные загрузили из старой копии. Объекта-приёмника уже нет, и связи рвутся.
  • ⚙️ Устаревший код в расширении или внешней обработке. Он пишет в реквизит значение, полученное по GUID, не проверяя, существует ли объект.

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

Битые ссылки в СКД: отчёт показывает пустые строки

Схема компоновки данных выводит битую ссылку как <Объект не найден> или как пустую группировку. Если отчёт строится по измерению регистра, сумма по такой строке попадает в итог, но понять, к чему она относится, нельзя.

Как обработать это в наборе данных:

  1. В запросе набора данных сделайте левое соединение с таблицей объекта, как в шаблоне выше.
  2. Добавьте вычисляемое поле признака: ВЫБОР КОГДА Спр.Ссылка ЕСТЬ NULL И Т.Склад <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК БитаяСсылка.
  3. Для читаемого вывода замените представление: ЕСТЬNULL(Спр.Наименование, "Удалённый объект").
  4. В настройках варианта добавьте отбор или условное оформление по полю БитаяСсылка, чтобы такие строки сразу бросались в глаза.

СКД оптимизирует запрос и может убрать из него соединение, поля которого не участвуют в выводе. Если признак БитаяСсылка не выведен и не используется в отборе, проверка просто не выполнится. Держите поле в выбранных или в отборе, либо вынесите условие в секцию ГДЕ запроса набора.

Отчёт с таким признаком удобно оставить постоянным: бухгалтер увидит проблему сам, не дожидаясь, пока сломается проведение.

Как исправить битую ссылку: три способа

Способ выбирают по тому, можно ли вернуть исходный объект.

СпособКогда подходитРиски
Восстановить объект с тем же GUIDОбъект есть в резервной копии или в другой базеМинимальные: все ссылки «оживают» сразу
Заменить ссылку на правильный объектИсходный объект не нужен, есть заменаПерепроведение документов меняет движения
Тестирование и исправление в конфигуратореБитых ссылок много и разбирать их по одной нереальноСоздаёт пустые объекты-заглушки, их потом надо заполнить

Перед любым из способов сделайте резервную копию базы. Исправления затрагивают закрытые периоды, и откат без копии невозможен.

Способ 1. Восстановить объект с исходным GUID

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

// БитаяСсылка — значение из результата поиска
НовыйЭлемент = Справочники.Контрагенты.СоздатьЭлемент();
НовыйЭлемент.УстановитьСсылкуНового(БитаяСсылка);
НовыйЭлемент.Наименование = "Восстановленный контрагент";
// заполните ИНН и остальные реквизиты из резервной копии
НовыйЭлемент.Записать();

Если у вас есть только строка GUID, получите ссылку так: Справочники.Контрагенты.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID)). Реквизиты возьмите из резервной копии, развернутой в отдельную базу: найдите там объект по тому же GUID.

Способ 2. Заменить битую ссылку на правильную

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

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

Способ 3. Тестирование и исправление информационной базы

Запустите конфигуратор, откройте Администрирование → Тестирование и исправление. Отметьте проверку ссылочной целостности и выберите действие для ссылок на несуществующие объекты:

  • ✅ создавать объекты — платформа создаст заглушки с исходными GUID, документы останутся связаны;
  • ✅ очищать ссылки — реквизиты станут пустыми, связь потеряется навсегда;
  • ✅ не изменять — только отчёт о найденных проблемах, полезен для оценки масштаба.

Режим требует монопольного доступа. Все пользователи должны выйти из базы, их несохранённые данные пропадут. Запускайте вечером или в выходной.

Начните с режима «не изменять»: он покажет, сколько битых ссылок и в каких таблицах. Потом решите, какие объекты восстанавливать по GUID вручную, а какие отдать на заглушки.

Если битые ссылки появляются снова

Разовое исправление не поможет, если источник остался. Проверьте по очереди:

  • 🔍 Права на интерактивное удаление. У обычных пользователей его быть не должно, удаление только через пометку и штатную обработку.
  • 🔍 Свои обработки и расширения. Ищите в коде вызовы Удалить() у объектов и записи с ОбменДанными.Загрузка = Истина: в этом режиме проверки при записи отключаются.
  • 🔍 Обмены. Если битые ссылки появляются после каждого сеанса и висят дольше следующего, в правилах обмена не выгружается какой-то справочник.
  • 🔍 Регламентные задания. Журнал регистрации покажет, какой фоновый сеанс удалял данные.

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

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

Почему битая ссылка не считается пустой при проверке заполнения?
В реквизите хранится ненулевой GUID, поэтому платформа считает значение заполненным. Проверка заполнения пропускает такой документ.
Можно ли найти битые ссылки без конфигуратора и программирования?
Можно через консоль запросов в режиме «1С:Предприятие»: вставьте шаблон с левым соединением, подставьте свою таблицу и реквизит. Права администратора всё равно понадобятся.
Чем битая ссылка отличается от ссылки на помеченный на удаление объект?
Битая ссылка означает, что объекта физически нет в таблице. Помеченный на удаление объект существует, просто имеет признак пометки, и ссылка на него рабочая.
Создавать ли заглушки в «Тестировании и исправлении», если исходных данных нет?
Да, если важно сохранить связь между документами: заглушку потом заполнят или заменят. Очистка ссылок необратима.
Влияет ли битая ссылка на скорость работы базы?
Нет. Она влияет на корректность данных: остатки, отчёты, печатные формы и обмены.