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