При переносе информационной базы из файлового режима в клиент-серверный вариант (MS SQL Server) через файл выгрузки .dt администраторы и разработчики нередко сталкиваются со сбоем импорта. Платформа прерывает загрузку сообщением об ошибке: Infobase loading error. Some data was not loaded into the Infobase, reason: Attempt to insert nonunique values into unique index, указывая на сбой создания уникального индекса CREATE UNIQUE INDEX terminated because a duplicate key was found.
Разберем детально механизм возникновения этой проблемы, расшифруем сигнатуру ошибки в СУБД и рассмотрим пошаговые сценарии ее гарантированного устранения.
Проанализируем техническую сторону процесса загрузки данных платформой 1С:Предприятие. При развертывании базы из дампа .dt платформа сначала создает структуру таблиц в СУБД без уникальных индексов (или с отключенными индексами). Затем выполняется потоковая массовая вставка строк (bulk insert). После завершения вставки данных платформа подает серверу СУБД команду построения индексов, включая уникальные: CREATE UNIQUE INDEX.
Если среди загруженных данных физически присутствуют повторяющиеся строки с одинаковым набором ключевых полей, Microsoft SQL Server отклоняет команду создания индекса и полностью откатывает транзакцию загрузки. В файловом формате (1CD) движок базы данных менее строг: при аварийных сбоях питания, обрывах соединений или падениях рабочих процессов внутренние индексы B-дерева могут разрушаться, в результате чего в таблице накапливаются дублирующиеся строки без блокировки работы пользователей.
Посмотрим на текст ошибки из журнала:
The duplicate key value is (Jan 19 4016 12:00PM, 0x000000aa, 0x944d001f3c2a35f111e5f56a1bd0b2e9, 1).
Имя объекта — dbo._AccRg349, имя индекса — _AccRg349_ByPeriod_TRN.
Выясним причину каждого элемента этой сигнатуры:
dbo._AccRg349: Это физическая таблица движений регистра бухгалтерии (не таблица итогов). Выяснить точное имя регистра в конфигурации можно с помощью метода ПолучитьСтруктуруХраненияБазыДанных()._AccRg349_ByPeriod_TRN: Составной уникальный индекс регистра бухгалтерии, обеспечивающий уникальность по полям: Период (_Period), Тип регистратора (_RecorderTRef), Ссылка регистратора (_RecorderRRef) и Номер строки (_LineNo).Jan 19 4016 12:00PM: При использовании MS SQL со стандартным смещением 2000 лет реальная дата документа в 1С соответствует 19.01.2016 12:00:00.0x000000aa: Код типа метаданных регистратора (в шестнадцатеричном виде, 170 в десятичном формате).0x944d001f3c2a35f111e5f56a1bd0b2e9: Двоичный идентификатор GUID конкретного документа-регистратора.1: Номер строки движения (НомерСтроки).Таким образом, в регистре бухгалтерии по одному и тому же документу образовались две или более записи с одинаковым периодом и одинаковым номером строки 1.
Рассмотрим порядок действий для устранения конфликта уникальности в зависимости от доступных исходных данных.
Это самый быстрый и надежный способ, решающий проблему в подавляющем большинстве случаев:
.dt в новую пустую файловую информационную базу. Так как файловая СУБД 1С более лояльна к дубликатам при импорте, загрузка выгрузки пройдет успешно.chdbfl.exe (находится в каталоге платформы bin), выберем файл 1Cv8.1CD и отметим флаг Исправлять обнаруженные ошибки. Это восстановит поврежденную структуру внутренних страниц файла..dt и выполним повторную загрузку в клиент-серверную базу на MS SQL Server.Если требуется точно локализовать документ без полного тестирования всей базы, найдем и перезапишем движения программно. Разберем код внешней обработки:
// Определим ссылку на документ по известному строковому представлению GUID
ИдентификаторGUID = Новый УникальныйИдентификатор("944d001f-3c2a-35f1-11e5-f56a1bd0b2e9");
// Ищем документ среди документов соответствующего типа или через универсальный запрос
СсылкаНаДокумент = Документы.ОперацияБух.ПолучитьСсылку(ИдентификаторGUID);
// Создаем набор записей регистра бухгалтерии с отбором по документу-регистратору
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
// Считываем движения из базы данных
НаборЗаписей.Прочитать();
// Принудительно восстанавливаем уникальную сквозную нумерацию строк
Номер = 1;
Для Каждого Запись Из НаборЗаписей Цикл
Запись.НомерСтроки = Номер;
Номер = Номер + 1;
КонецЦикла;
// Перезаписываем набор с замещением существующих записей
НаборЗаписей.Записать(Истина);
Сообщить("Набор записей успешно перезаписан, дубли устранены.");
Метод Записать(Истина) очищает старые записи регистра по регистратору и записывает нормализованный набор, устраняя конфликт номеров строк в индексе _ByPeriod_TRN.
Учитывая особенности работы платформы, обратим внимание на следующие правила:
.dt предназначен исключительно для переноса данных между различными платформами СУБД (например, с файловой на MS SQL или PostgreSQL). Не используем выгрузку в .dt в качестве штатного резервного копирования промышленной базы. Для бэкапов серверных баз следует использовать средства MS SQL Server (регулярные полные и разностные бэкапы, бэкапы транзакционных логов)..dt для миграции всегда выполняем предварительную проверку: логическую реиндексацию и исправление ошибок штатными средствами конфигуратора..dt используем обработку Выгрузка и загрузка данных XML либо штатные механизмы планов обмена. Загрузка через объектную модель 1С самостоятельно нормализует наборы движений при их записи.