Ошибка СУБД с кодом 23505 (ERROR: could not create unique index) — это классическая проблема, с которой сталкиваются администраторы и программисты 1С при выполнении тестирования и исправления (ТиИ), реструктуризации базы данных или обновлении конфигурации. Суть проблемы заключается в том, что в таблице данных уже существуют записи, которые нарушают правила уникальности нового или обновляемого индекса. Система пытается создать индекс, видит дубли и прерывает операцию.
Особенно часто это проявляется в связке 1С:Предприятие + PostgreSQL. Разберем детально, почему это происходит и как вылечить базу данных, сохранив при этом целостность информации.
Для начала проанализируем текст ошибки, который обычно выглядит так:
could not create unique index "_document410_vt12786_skng" DETAIL: Key (_fld969, _document410_idrref, _keyfield)=(0, \xa10e70..., \x00000000) is duplicated.
Разберем это сообщение по составляющим, чтобы понять, где именно «закопана» проблема:
_document означает документ, а _vt указывает на то, что ошибка произошла в конкретной табличной части этого документа.\x00000000 часто указывает на то, что у нескольких строк этот ключ «обнулился» или не был корректно назначен.Проблема возникает, когда в одной табличной части одного документа оказываются две и более строки с абсолютно одинаковым набором ключевых полей. Пока индекса нет, данные «лежат» в базе спокойно, но как только 1С пытается перестроить индексы (например, при обновлении или ТиИ), СУБД блокирует создание уникального индекса из-за этих дублей.
Как показывает практика и сообщения пользователей, наиболее простым и эффективным методом является временный переход в файловый режим. Рассмотрим этот алгоритм по шагам:
.dt..dt в эту файловую базу. Файловый движок 1С более лояльно относится к отсутствию индексов в момент загрузки..dt..dt обратно в клиент-серверную базу на PostgreSQL.Этот метод хорош тем, что механизмы платформы 1С сами пересчитывают поле _keyfield и устраняют логические несоответствия без прямого вмешательства в таблицы СУБД.
Если база слишком большая и манипуляции с .dt занимают сутки, нам поможет прямой запрос к базе данных. Проанализируем ситуацию в консоли PostgreSQL (pgAdmin или psql). Чтобы найти конкретный документ, в котором «размножились» строки, выполним следующий запрос:
SELECT _document410_idrref, _fld969, _keyfield, COUNT(*)
FROM _document410_vt12786
GROUP BY _document410_idrref, _fld969, _keyfield
HAVING COUNT(*) > 1;
В результате мы получим _idrref документа. Однако в СУБД он хранится в бинарном формате. Чтобы найти этот документ в интерфейсе 1С, нам нужно преобразовать его в понятный GUID. Рассмотрим пример функции на языке 1С, которая поможет найти объект, если вы знаете его _idrref в виде строки из SQL:
// Пример поиска объекта по строковому представлению GUID из SQL
ИдентификаторСтрокой = "a10e7071bccd52c111e968c89c9b7c53"; // значение из DETAIL ошибки
НужныйGUID = Новый УникальныйИдентификатор(ИдентификаторСтрокой);
СсылкаНаДокумент = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(НужныйGUID);
Если Не СсылкаНаДокумент.ПолучитьОбъект() = Неопределено Тогда
Сообщить("Найден проблемный документ: " + СсылкаНаДокумент);
КонецЕсли;
После того как документ найден, попробуем просто перепровести его или перезаписать. При записи объекта 1С автоматически попытается упорядочить строки табличной части и обновить поле _keyfield. Если обычная запись не помогает, можно программно прочитать табличную часть, очистить её, и загрузить данные назад, после чего выполнить ЭтотОбъект.Записать().
Иногда ошибка является «ложной» и вызвана особенностями работы движка PostgreSQL (MVCC). Когда данные обновляются, старые версии строк помечаются как удаленные, но физически остаются в таблице до очистки. В редких случаях индекс пытается учесть эти «мертвые» строки.
Проанализируем состояние таблицы и выполним полную очистку:
VACUUM FULL ANALYZE _document410_vt12786;. Это действие полностью перестроит таблицу на диске, удалив весь «мусор».REINDEX TABLE _document410_vt12786;.Важно: Команда VACUUM FULL блокирует таблицу целиком, поэтому выполнять её нужно в период отсутствия пользователей в базе.
Если мы точно уверены, что в таблице присутствуют полные дубликаты строк (одинаковые данные, которые не должны там быть), и мы не можем их исправить через 1С, можно прибегнуть к «хирургическому» методу в SQL. В PostgreSQL у каждой строки есть скрытое поле ctid, которое уникально для физического расположения записи.
Рассмотрим пример удаления одной из дублирующихся строк, оставляя при этом вторую такую же:
DELETE FROM _document410_vt12786
WHERE ctid IN (
SELECT ctid FROM (
SELECT ctid, ROW_NUMBER() OVER (
PARTITION BY _document410_idrref, _fld969, _keyfield
ORDER BY ctid
) as row_num
FROM _document410_vt12786
) t
WHERE t.row_num > 1
);
Этот запрос проанализирует все записи, сгруппирует их по ключевым полям и удалит те, что имеют порядковый номер больше одного в рамках своей группы. Перед выполнением этой операции обязательно сделайте бэкап базы данных!
Выясним, как избежать подобных ошибок в будущем:
ANALYZE и VACUUM. Это поможет поддерживать актуальность статистик и чистоту таблиц.Проблема с неуникальными значениями — это всегда следствие некорректного завершения транзакций или программных сбоев при записи объектов. Системный подход (поиск через SQL -> исправление в 1С -> финализация через ТиИ) позволяет решить её в 100% случаев. Для дополнительного контроля и поиска расхождений после восстановления данных используется автоматическая сверка бухгалтерских и налоговых проводок между базами 1С.