Как удалить записи из регистра сведений, если регистратор удален (Объект не найден)?

Программист 1С v8.3 (Обычные формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

В процессе эксплуатации и администрирования информационных баз «1С:Предприятие» разработчики часто сталкиваются с проблемой нарушения ссылочной целостности. Типичный сценарий: после некорректного обмена данными, сбоя в работе распределенной информационной базы (РИБ) или прямого удаления объектов без контроля ссылок в подчиненном регистре сведений остаются записи («движения»), тогда как сам документ-регистратор физически отсутствует в базе. В интерфейсе программы в поле Регистратор мы видим надпись <Объект не найден> (вместе с его уникальным идентификатором GUID).

Попытка удалить такие записи вручную или через стандартные обработки очистки часто приводит к системной ошибке: «Не установлен отбор по регистратору». Платформа 1С требует обязательного указания конкретного регистратора для записи или удаления набора в подчиненных регистрах, но самого объекта в базе нет. В этой статье мы подробно разберем несколько эффективных способов решения данной проблемы — от простых программных методов до прямого вмешательства на уровне СУБД.

Причина возникновения проблемы

В платформе «1С:Предприятие» регистры сведений могут быть независимыми или подчиненными регистратору. Для подчиненных регистров режим записи жестко привязан к документу, который выступает в роли регистратора. Записи в таких регистрах не могут существовать «сами по себе» — они физически и логически связаны с таблицей документов. Когда документ удаляется в обход стандартных механизмов (например, через непосредственное удаление в коде без очистки движений или при выборочном переносе данных через планы обмена), физические записи в таблице регистра остаются «сиротами». Платформа не может сопоставить их с существующим документом, что вызывает ошибки при любых попытках модификации набора данных.


Метод 1. Программная очистка набора записей с отключением обработчиков

Самый правильный и безопасный с точки зрения платформы способ — это программное создание пустого набора записей с установкой отбора по «битой» ссылке. Однако здесь есть критически важный нюанс, который часто упускают разработчики.

Если мы просто попытаемся записать пустой набор, платформа по умолчанию запустит обработчики событий регистра (такие как ПередЗаписью или ПриЗаписи в модуле регистра сведений), а также зарегистрированные подписки на события. В типовых конфигурациях (особенно в «1С:Зарплата и управление персоналом») внутри этих обработчиков почти всегда происходит обращение к реквизитам документа-регистратора (например, получение даты, организации или проведение дополнительных проверок). Так как документа нет, обращение к его свойствам вызовет ошибку «Значение не является значением объектного типа», и транзакция откатится.

Чтобы избежать этого, необходимо принудительно отключить выполнение обработчиков событий с помощью свойства ОбменДанными.Загрузка = Истина. Рассмотрим по шагам готовый код для удаления таких записей:


Процедура ОчиститьЗаписиБитыхРегистраторов()
    
    ИмяРегистра = "ВашРегистрСведений"; // Укажите имя вашего подчиненного регистра
    
    // Шаг 1. Находим все "битые" ссылки регистраторов через запрос
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   ТаблицаРегистра.Регистратор КАК БитыйРегистратор
        |ИЗ
        |   РегистрСведений." + ИмяРегистра + " КАК ТаблицаРегистра
        |ГДЕ
        |   ТаблицаРегистра.Регистратор.Ссылка ЕСТЬ NULL 
        |   И НЕ ТаблицаРегистра.Регистратор = &ПустаяСсылка";
        
    // Нам не нужны пустые ссылки типов документов, только битые ссылки с GUID
    Запрос.УстановитьПараметр("ПустаяСсылка", Неопределено); 
    
    РезультатЗапроса = Запрос.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        Сообщить("Битых ссылок в регистре не обнаружено.");
        Возврат;
    КонецЕсли;
    
    Выборка = РезультатЗапроса.Выбрать();
    
    // Шаг 2. Обходим выборку и очищаем наборы записей
    Пока Выборка.Следующий() Цикл
        
        Сообщить("Очистка движений для регистратора: " + Строка(Выборка.БитыйРегистратор));
        
        // Создаем набор записей для конкретного регистратора
        НаборЗаписей = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.Регистратор.Установить(Выборка.БитыйРегистратор);
        
        // Включаем режим загрузки, чтобы обойти проверки и обработчики событий
        НаборЗаписей.ОбменДанными.Загрузка = Истина;
        
        Попытка
            // Записываем пустой набор записей, что физически удаляет движения из СУБД
            НаборЗаписей.Записать();
            Сообщить("Записи успешно удалены.");
        Исключение
            Сообщить("Не удалось очистить записи: " + ОписаниеОшибки(), СтатусСообщения.Важное);
        КонецПопытки;
        
    КонецЦикла;
    
КонецПроцедуры

Метод 2. Создание временного документа-«заглушки» через восстановление GUID

Иногда возникают ситуации, когда конфигурация базы настолько чувствительна к целостности данных, что прямое удаление набора записей заблокировано логикой СУБД или бизнес-правилами. В этом случае лучшим решением будет временное создание реального документа с тем же уникальным идентификатором (GUID), который имеет «битая» ссылка.

После создания такого документа система перестанет писать <Объект не найден> и привяжет существующие записи регистра к вновь созданному документу. Затем мы сможем штатно очистить движения документа и пометить его на удаление. Проанализируем, как это сделать программно:


Процедура ВосстановитьРегистраторИОчистить(СтрокаGUID, ТипДокумента)
    
    // СтрокаGUID - строковое представление уникального идентификатора битой ссылки
    // Пример: "463ffaea-bffe-11dc-8564-00e04cec75b9"
    // ТипДокумента - имя документа в метаданных, например, "ПриемНаРаботу"
    
    СсылкаНаОбъект = Документы[ТипДокумента].ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID));
    
    // Проверяем, действительно ли объект отсутствует в базе данных
    Если СсылкаНаОбъект.ПолучитьОбъект() = Неопределено Тогда
        
        // Создаем новый документ-заглушку
        НовыйДокумент = Документы[ТипДокумента].СоздатьДокумент();
        
        // Принудительно устанавливаем ему нужную ссылку (GUID) перед первой записью
        НовыйДокумент.УстановитьСсылкуНового(СсылкаНаОбъект);
        
        // Заполняем минимально необходимые реквизиты для возможности записи
        НовыйДокумент.Дата = ТекущаяДата();
        НовыйДокумент.Комментарий = "Временный документ-заглушка для очистки движений";
        
        Попытка
            // Записываем как непроведенный
            НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
            Сообщить("Создан документ-заглушка: " + Строка(СсылкаНаОбъект));
            
            // Теперь, когда документ существует, очищаем движения стандартным способом
            Набор = РегистрыСведений.ВашРегистрСведений.СоздатьНаборЗаписей();
            Набор.Отбор.Регистратор.Установить(СсылкаНаОбъект);
            Набор.Записать(); // Записываем пустой набор
            
            // Помечаем временный документ на удаление
            ДокументОбъект = СсылкаНаОбъект.ПолучитьОбъект();
            ДокументОбъект.УстановитьПометкуУдаления(Истина);
            Сообщить("Движения очищены, документ помечен на удаление.");
            
        Исключение
            Сообщить("Ошибка при создании заглушки: " + ОписаниеОшибки(), СтатусСообщения.Важное);
        КонецПопытки;
        
    Иначе
        Сообщить("Документ существует в базе данных, создание заглушки не требуется.");
    КонецЕсли;
    
КонецПроцедуры

Метод 3. Восстановление исходных данных из другой базы (для систем обмена)

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

Для этого мы можем выгрузить список всех «битых» GUID из базы-приемника, найти эти объекты в базе-отправителе по их уникальным идентификаторам, выгрузить их во внешний файл xml с помощью стандартной обработки ВыгрузкаЗагрузкаДанных82.epf (или аналогичной для вашей версии платформы) и загрузить в базу-приемник. После загрузки документов ссылочная целостность полностью восстановится автоматически.


Метод 4. Использование стандартного механизма «Тестирование и исправление»

Платформа 1С содержит мощный встроенный инструмент для борьбы с битыми ссылками. Чтобы воспользоваться им, выполните следующие действия:

  1. Обязательно создайте резервную копию (бэкап) информационной базы перед проведением любых процедур.
  2. Запустите базу в режиме Конфигуратор.
  3. Перейдите в меню Администрирование — Тестирование и исправление информационной базы....
  4. Установите флаг напротив проверки: «Проверка ссылочной целостности информационной базы».
  5. В правой части окна в группе настроек «При наличии ссылок на несуществующие объекты» выберите один из вариантов решения:
    • «Создавать объекты» — платформа автоматически создаст пустые документы-заглушки для всех найденных битых ссылок. После этого вы сможете удалить их стандартными методами.
    • «Очищать ссылки» — платформа попытается самостоятельно удалить движения, ссылающиеся на отсутствующие документы.
  6. Нажмите кнопку «Выполнить» и дождитесь окончания процесса.

Обратите внимание: На больших базах данных процедура тестирования и исправления может занимать значительное время (до нескольких суток) и требует монопольного доступа к базе данных.


Метод 5. Прямое удаление записей на уровне СУБД (для баз данных на MS SQL или PostgreSQL)

Если ваша информационная база работает в клиент-серверном варианте, а объем «зависших» записей в регистре исчисляется миллионами строк, программные методы 1С могут выполняться медленно и вызывать критические транзакционные блокировки. В таком случае проблему можно оперативно решить на уровне базы данных SQL.

Рассмотрим алгоритм действий системного администратора:

  1. С помощью метода платформы ПолучитьСтруктуруХраненияБазыДанных() определите физическое имя таблицы нужного регистра сведений в СУБД. Имя таблицы будет иметь формат вида _InfoRgXXXX.
  2. В таблицах подчиненных регистров ссылка на регистратор хранится в виде двух служебных колонок:
    • _RecorderTRef — идентификатор типа метаданных документа.
    • _RecorderRRef — уникальный двоичный идентификатор (GUID) самого документа.
  3. Составьте и выполните SQL-запрос для очистки записей. Пример запроса для MS SQL Server:

-- Шаг 1. Сначала рекомендуется выполнить SELECT, чтобы проверить, какие данные будут удалены
SELECT * 
FROM _InfoRg1234 -- Вместо _InfoRg1234 укажите физическое имя вашей таблицы регистра
WHERE _RecorderRRef = 0x856400E04CEC75B9 -- Шестнадцатеричное представление GUID битой ссылки

-- Шаг 2. Непосредственное удаление записей
DELETE 
FROM _InfoRg1234 
WHERE _RecorderRRef = 0x856400E04CEC75B9

Важное предупреждение: Любое прямое изменение данных в таблицах SQL минуя сервер приложений 1С нарушает лицензионное соглашение и может привести к логическому повреждению базы данных при неаккуратном использовании. Выполняйте эти действия только при наличии актуального бэкапа. После выполнения SQL-запроса обязательно перезапустите службу сервера «1С:Предприятие» или очистите кэш сервера, чтобы обновить кэшированные данные платформы.


Важный нюанс диагностики: Права доступа и механизм RLS

Иногда надпись <Объект не найден> в поле регистратора появляется не из-за физического отсутствия документа, а из-за ограничений прав доступа на уровне записей (RLS — Record Level Security). Например, если текущему пользователю запрещен доступ к организации, по которой проведен документ, платформа скроет данные документа и отобразит битую ссылку.

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

← На главную