Как исправить ошибку нарушения уникальности при загрузке базы 1С в PostgreSQL?

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

При переносе базы данных 1С из файлового режима в клиент-серверный на СУБД PostgreSQL администраторы и разработчики часто сталкиваются со следующей критической ошибкой:

«Ошибка загрузки информационной базы. В информационную базу загружены не все данные по причине: Нарушено условие уникальности данных. Попытка вставки неуникального значения в уникальный индекс: ERROR: could not create unique index...»

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

Почему ошибка возникает в PostgreSQL, но отсутствует в файловой базе?

Это классический парадокс платформы 1С. Файловая база данных (формат .1cd) использует собственный встроенный движок СУБД. Настройки контроля уникальности и записи данных в файловой СУБД в ряде режимов (например, при обмене данными с установленным свойством ОбменДанными.Загрузка = Истина) могут быть лояльны к некоторым несоответствиям структуры. В результате в таблице физически могут появиться записи с одинаковыми ключевыми полями индекса.

При этом стандартное «Тестирование и исправление» (ТИИ) и утилита проверки целостности chdbfl.exe часто рапортуют о том, что база абсолютно здорова. Проблема «всплывает» только при попытке переезда на клиент-серверную архитектуру:

  1. При импорте файла .dt платформа 1С последовательно создает таблицы в базе данных PostgreSQL и загружает в них «сырые» данные.
  2. После успешной заливки строк в таблицу платформа отправляет СУБД PostgreSQL SQL-запрос на создание уникальных индексов (команда вида CREATE UNIQUE INDEX).
  3. Поскольку СУБД PostgreSQL жестко соблюдает требования реляционной теории и стандартов SQL, при обнаружении даже одной пары дублирующихся строк по ключевым полям индекса процесс прерывается ошибкой. Транзакция откатывается, и загрузка базы завершается аварийно.

Выясним причину этой проблемы и рассмотрим по шагам, как найти и исправить поврежденные данные.

Шаг 1. Локализация проблемного объекта метаданных

Проанализируем текст ошибки, с которой столкнулся пользователь:

ERROR: could not create unique index "_reference323_4" DETAIL: Key (_fld1080, _code, _idrref)=(0, 000000004, \x8f4f20cf30e2c00a11eacc7c9724c348) is duplicated.

Из этого сообщения мы можем извлечь следующие технические сведения:

  1. Индекс строится по таблице _reference323. Префикс _reference указывает на то, что это таблица справочника.
  2. Дублирование произошло по ключевым полям: _fld1080 (разделитель данных или область данных, если используется разделение), _code (код элемента справочника) и _idrref (внутренний уникальный идентификатор GUID).

Чтобы понять, какому именно справочнику в конфигураторе соответствует таблица _reference323, нам потребуется запустить файловую базу в режиме «1С:Предприятие» и воспользоваться встроенным методом платформы ПолучитьСтруктуруХраненияБазыДанных(). Для этой задачи можно использовать обработку для анализа структуры хранения базы данных.

Для этого создадим простую внешнюю обработку и выполним следующий код в модуле:


ТаблицаСтруктуры = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого СтрокаТаблицы Из ТаблицаСтруктуры Цикл
    Если ВРег(СтрокаТаблицы.ИмяТаблицыХранения) = "_REFERENCE323" Тогда
        Сообщить("Имя объекта в конфигурации: " + СтрокаТаблицы.ИмяТаблицы);
        Прервать;
    КонецЕсли;
КонецЦикла;

В результате выполнения этого кода мы получим точное представление о проблемном объекте. В рассматриваемом примере этим объектом оказался справочник Справочник.ВидыОбъектовЕГАИС.

Шаг 2. Выявление причин дублирования предопределенных элементов

Разберем подробнее, почему в справочнике ВидыОбъектовЕГАИС (или аналогичных системных справочниках ГИСМ, ВетИС, классификаторах банков и валют) появляются дубли. Чаще всего это происходит по следующим причинам:

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

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

Шаг 3. Программный поиск дублей через запрос

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


ВЫБРАТЬ
	ЕГАИС.ИмяПредопределенныхДанных КАК ИмяПредопределенныхДанных,
	КОЛИЧЕСТВО(ЕГАИС.Ссылка) КАК КоличествоДублей
ИЗ
	Справочник.ВидыОбъектовЕГАИС КАК ЕГАИС
ГДЕ
	ЕГАИС.ИмяПредопределенныхДанных <> ""
СГРУППИРОВАТЬ ПО
	ЕГАИС.ИмяПредопределенныхДанных
ИМЕЮЩИЕ
	КОЛИЧЕСТВО(ЕГАИС.Ссылка) > 1

Проанализируем ситуацию: если запрос вернул строки, значит, в базе действительно присутствуют физические дубли предопределенных данных, которые блокируют создание уникального индекса СУБД.

Шаг 4. Программное удаление и «отвязка» дубликатов

Попытка удалить предопределенный элемент справочника вручную через интерфейс пользователя 1С или стандартной обработкой удаления объектов завершится неудачей — платформа заблокирует это действие, так как элемент связан с метаданными конфигурации.

Чтобы решить эту задачу, нам необходимо программно «отвязать» дублирующиеся элементы от метаданных конфигуратора, превратив их в обычные элементы справочника. Для этого выполним следующий алгоритм в файловой копии базы данных:


// Шаг 4.1. Находим дубликаты предопределенных элементов
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЕГАИС.Ссылка КАК Ссылка,
    |	ЕГАИС.ИмяПредопределенныхДанных КАК ИмяПредопределенныхДанных
    |ИЗ
    |	Справочник.ВидыОбъектовЕГАИС КАК ЕГАИС
    |ГДЕ
    |	ЕГАИС.ИмяПредопределенныхДанных В
    |		(ВЫБРАТЬ
    |			ВТ.ИмяПредопределенныхДанных
    |		ИЗ
    |			Справочник.ВидыОбъектовЕГАИС КАК ВТ
    |		ГДЕ
    |			ВТ.ИмяПредопределенныхДанных <> """"
    |		СГРУППИРОВАТЬ ПО
    |			ВТ.ИмяПредопределенныхДанных
    |		ИМЕЮЩИЕ
    |			КОЛИЧЕСТВО(ВТ.Ссылка) > 1)";

РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    
    // Используем соответствие для сохранения первого "правильного" элемента
    // и очистки последующих дубликатов
    ОбработанныеИмена = Новый Соответствие;
    
    Пока Выборка.Следующий() Цикл
        ИмяПредопределенного = Выборка.ИмяПредопределенныхДанных;
        
        Если ОбработанныеИмена[ИмяПредопределенного] = Неопределено Тогда
            // Это первый встреченный элемент, его мы оставляем как основной
            ОбработанныеИмена.Вставить(ИмяПредопределенного, Выборка.Ссылка);
            Сообщить("Оставляем как основной: " + Выборка.Ссылка + " (" + ИмяПредопределенного + ")");
        Иначе
            // Это дубликат. Снимаем признак предопределенности
            ОбъектДубль = Выборка.Ссылка.ПолучитьОбъект();
            ОбъектДубль.ИмяПредопределенныхДанных = "";
            Попытка
                ОбъектДубль.Записать();
                Сообщить("Успешно 'отвязан' от метаданных дубликат: " + Выборка.Ссылка);
            Исключение
                Сообщить("Не удалось перезаписать объект: " + ОписаниеОшибки());
            КонецПопытки;
        EndЕсли;
    КонецЦикла;
КонецЕсли;

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

Шаг 5. Объединение ссылок и окончательная очистка

После того как дубликаты стали обычными элементами, мы не можем просто так удалить их из базы, так как на них могут ссылаться существующие документы или регистры сведений (например, документы ЕГАИС).

Для корректного завершения процедуры выполним следующие действия:

  1. Откроем стандартную обработку из состава Библиотеки Стандартных Подсистем (БСП) «Поиск и удаление дублей».
  2. Укажем в качестве объекта поиска справочник Виды объектов ЕГАИС.
  3. В качестве правила поиска выберем совпадение по реквизиту Наименование или Код.
  4. В найденных парах назначим «оригиналом» тот элемент, у которого сохранился признак предопределенного, а дубликат сделаем «дублем».
  5. Нажмем кнопку «Удалить дубли». При этом система автоматически перенесет все ссылки со старого некорректного элемента на правильный предопределенный элемент во всех таблицах базы данных и безболезненно удалит дубль.

Шаг 6. Контрольное обслуживание и перенос в PostgreSQL

После успешного слияния дубликатов выполним завершающие регламентные операции в файловой базе. Для автоматизации регламентного обслуживания и резервного копирования нескольких информационных баз можно использовать автоматическое архивирование и тестирование баз в Обновляторе 1С.

  1. Запустим режим «Тестирование и исправление» со следующими включенными опциями:
    • Реиндексация таблиц информационной базы.
    • Проверка логической целостности.
    • Проверка ссылочной целостности.
  2. Выгрузим информационную базу в файл резервной копии (Администрирование -> Выгрузить информационную базу...), получив чистый файл .dt.
  3. Подключимся к серверу PostgreSQL, создадим новую пустую базу данных и запустим ее в режиме «Конфигуратор».
  4. Выполним операцию «Загрузить информационную базу...» и выберем наш подготовленный файл .dt.

Поскольку все дубликаты записей по уникальным ключевым полям были успешно удалены в файловой копии, СУБД PostgreSQL без ошибок выполнит SQL-запрос CREATE UNIQUE INDEX, и загрузка завершится успешно.

← На главную