При переносе информационной базы из файлового варианта в клиент-серверный на базе PostgreSQL системные администраторы часто сталкиваются с ошибкой: «Нарушено условие уникальности данных. Попытка вставки неуникального значения в уникальный индекс». Эта проблема особенно актуальна для баз, использующих механизм распределенных информационных баз (РИБ). В этой статье мы разберем причины возникновения этой ошибки, научимся идентифицировать проблемные объекты и рассмотрим пошаговый алгоритм восстановления целостности данных.
Проанализируем ситуацию: файловая СУБД 1С (файл .1CD) обладает менее строгими механизмами контроля уникальности индексов по сравнению с промышленными СУБД, такими как PostgreSQL или MS SQL Server. В процессе работы, особенно при аварийных завершениях сеансов или некорректных обменах в РИБ, в файловой базе могут появиться записи с идентичными внутренними идентификаторами (UUID или GUID).
Когда мы пытаемся загрузить такую базу через файл выгрузки .dt в PostgreSQL, сервер СУБД при создании индексов обнаруживает дублирующиеся ключи в поле _idrref (первичный ключ таблицы) и блокирует операцию записи. Ошибка вида _reference99_pkey уже существует прямо указывает на то, что в таблице справочника (в данном примере с внутренним номером 99) есть две и более записи с одинаковым уникальным идентификатором.
Для начала нам нужно понять, какой именно справочник или документ скрывается под системным именем _reference99. Если база в клиент-серверном варианте не открывается, идентификацию следует проводить в исходной файловой базе. Рассмотрим способ определения через встроенный язык 1С.
Запустим файловую базу в режиме «Предприятие» и воспользуемся консолью запросов или создадим небольшую обработку, в которой выполним следующий код:
СтруктураХранения = ПолучитьСтруктуруХраненияБазыДанных();
Для Каждого Таблица Из СтруктураХранения Цикл
Если Таблица.ИмяТаблицыХранения = "_Reference99" Тогда
Сообщить("Конфликтный объект: " + Таблица.ИмяТаблицы);
Прервать;
КонецЕсли;
КонецЦикла;
Зная имя объекта (например, Справочник.Сотрудники), мы сможем прицельно искать дубликаты именно в нем.
Прежде чем использовать стандартное Тестирование и исправление (ТиИ), крайне важно выполнить проверку утилитой ChDBFL.exe. Она находится в каталоге установки платформы (папка bin). В отличие от ТиИ, эта утилита работает на низком уровне файловой структуры.
Разберем порядок действий:
1Cv8.1CD.ChDBFL.exe.Важный момент: часто именно повреждение индексов в файловой базе приводит к тому, что 1С «видит» записи как разные, хотя их UUID совпадают. ChDBFL.exe пересоберет дерево индексов, что может решить проблему еще до этапа выгрузки.
Если физическая проверка не помогла, значит, дубли существуют на логическом уровне. Проанализируем ситуацию: нам нужно найти записи, у которых внутренний идентификатор совпадает. Стандартными средствами (запросом по полю Ссылка) это сделать сложно, так как 1С будет возвращать разные ссылки, если индексы повреждены.
Рассмотрим пример кода для поиска дублей в конкретном справочнике:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Стр.Ссылка КАК Ссылка,
| Стр.Наименование КАК Наименование
|ИЗ
| Справочник.Сотрудники КАК Стр";
Результат = Запрос.Выполнить().Выгрузить();
ТаблицаУИД = Новый ТаблицаЗначений;
ТаблицаУИД.Колонки.Добавить("УИД", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(36)));
ТаблицаУИД.Колонки.Добавить("Ссылка");
Для Каждого Стр Из Результат Цикл
НоваяСтрока = ТаблицаУИД.Добавить();
НоваяСтрока.УИД = Строка(Стр.Ссылка.УникальныйИдентификатор());
НоваяСтрока.Ссылка = Стр.Ссылка;
КонецЦикла;
ТаблицаУИД.Сгруппировать("УИД", "Ссылка"); // Это не сработает для поиска дублей напрямую, используем другой подход:
ТаблицаУИД.Колонки.Добавить("Количество");
ТаблицаУИД.ЗаполнитьЗначения(1, "Количество");
Дубли = ТаблицаУИД.Скопировать();
Дубли.Сгруппировать("УИД", "Количество");
Для Каждого СтрДубль Из Дубли Цикл
Если СтрДубль.Количество > 1 Тогда
Сообщить("Найден дубль UUID: " + СтрДубль.УИД);
КонецЕсли;
КонецЦикла;
При обнаружении таких записей рекомендуется одну из них перезаписать с новым УникальныйИдентификатор(), предварительно перенеся ссылки со старого объекта на новый с помощью обработки «Поиск и замена дублей».
Многие пользователи совершают ошибку, выбирая режим «Очищать объекты» при обнаружении ошибок ссылочной целостности. Это приводит к тому, что документы «исчезают», а сотрудники «отвязываются» от организаций.
Проанализируем правильный подход к ТиИ:
.dt.Если перенос через .dt стабильно завершается ошибкой pkey, воспользуемся методом логического переноса данных. Рассмотрим работу с обработкой «Выгрузка и загрузка данных XML»:
Этот метод отличается тем, что он не копирует физические таблицы «как есть», а переносит данные в виде текстовых структур. При загрузке в пустую базу на PostgreSQL платформа заново создает все записи и индексы. Если в XML-файле встретятся объекты с одинаковыми UUID, система либо выдаст ошибку на конкретном объекте, либо (в зависимости от настроек) перезапишет существующий, что позволит избежать конфликта уникальности в СУБД.
Важно обратить внимание на версию платформы 1С. В версиях ветки 8.3.16 (в частности 8.3.16.1876) наблюдались специфические баги при работе с индексами в PostgreSQL. Проанализируем рекомендации по стабильным версиям:
standard_conforming_strings — он должен быть установлен в значение on.Подведем итоги. Чтобы успешно перенести базу на сервер и победить ошибку уникальности данных, выполним следующие шаги:
ChDBFL.exe с исправлением ошибок.ПолучитьСтруктуруХраненияБазыДанных().Следуя этому алгоритму, мы обеспечим сохранность данных и корректную работу индексов в новой клиент-серверной среде.