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

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

Работа с крупными базами данных на PostgreSQL иногда преподносит сюрпризы, которые программисты называют "магией". Представим ситуацию: вы запускаете простую обработку для синхронизации данных между РегистромСведений и справочником Номенклатура. Код написан верно, транзакции проходят без ошибок, но при проверке запросом вы видите, что данные остались прежними или "перепутались".

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

Суть проблемы: когда данные начинают "жить своей жизнью"

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

  1. Выборка данных из регистра через Запрос.
  2. Обход результата в цикле.
  3. Получение объекта номенклатуры через ПолучитьОбъект().
  4. Запись нового значения в Артикул и выполнение метода Записать().

После завершения обработки выясняется, что часть элементов (например, 55 из 7000) не обновилась. Повторные запуски обработки сообщают об успешной записи, но консоль запросов упрямо показывает старые или некорректные данные. Даже ручное изменение в карточке товара не спасает — в регистре данные "откатываются" или инвертируются.

Анализируем причину: откуда берутся дубли в уникальных индексах

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

  1. Некорректное резервное копирование: Использование инструментов pg_dump и pg_restore на "горячей" базе (когда пользователи активно записывают данные) без учета транзакционной целостности. Если бэкап снимался без блокировок, индексы и сами таблицы могут рассинхронизироваться.
  2. Ошибки файловой системы: Сбои в работе сервера, приведшие к повреждению файлов таблиц индексов.
  3. Специфика PostgreSQL: В редких случаях при высокой нагрузке механизмы MVCC (многоверсионность) могут допустить ситуацию, когда физически в таблице остаются две строки с одинаковыми ключевыми полями, хотя логически индекс должен это запрещать.

В результате при выполнении запроса 1С может получать то одну запись, то другую, в зависимости от того, какой план выполнения выбрала СУБД и какой индекс был задействован. Именно поэтому возникает ощущение, что значения "меняются местами".

Метод 1: Поиск дублей через консоль запросов 1С

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

Рассмотрим пример запроса для поиска проблемных мест:


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

Если этот запрос вернул строки — поздравляем, вы нашли причину "магии". Платформа видит несколько физических записей для одного измерения Номенклатура, чего в норме быть не должно.

Метод 2: Исправление через выгрузку и загрузку данных (.dt)

Это самый надежный "штатный" способ исправления логической целостности базы. Проанализируем, как это работает: при выгрузке в файл .dt платформа 1С последовательно считывает данные из таблиц. При последующей загрузке в новую (пустую) базу данных платформа заново создает структуру таблиц и индексов в PostgreSQL.

Важный нюанс: Если в данных действительно есть дубли, процесс загрузки .dt может прерваться с ошибкой "Запись с такими ключевыми полями уже существует". В этом случае платформа сама укажет на проблемный объект, и вам придется удалить лишние записи на уровне SQL, прежде чем повторить попытку.

Метод 3: Прямая работа с PostgreSQL (SQL-запросы)

Если база слишком велика для выгрузки в .dt, проблему можно решить непосредственно в pgAdmin или через консоль psql. Разберем шаги:

  1. Найдите имя таблицы вашего регистра в базе данных. Для этого в конфигураторе можно воспользоваться функцией ПолучитьСтруктуруХраненияБазыДанных().
  2. Выполните поиск дублей по полям измерений (которые в SQL обычно начинаются с _Fld).
  3. Удалите одну из записей, ориентируясь на системное поле ctid (физический адрес строки в PostgreSQL), чтобы оставить только одну версию данных.

Пример концептуального SQL-запроса для поиска дублей (имена полей будут зависеть от вашей конфигурации):


SELECT _fld123_idrref, COUNT(*) 
FROM _inforg456 
GROUP BY _fld123_idrref 
HAVING COUNT(*) > 1;

После удаления дублей крайне рекомендуется выполнить команду REINDEX TABLE имя_таблицы, чтобы перестроить индексы и убедиться, что ограничение уникальности снова работает корректно.

Программная обработка данных для устранения последствий

После того как дубли в регистре будут устранены, нам нужно гарантировать, что справочник Номенклатура содержит актуальные данные. Посмотрим на пример кода, который поможет безопасно обновить артикулы, исключая возможные ошибки блокировок:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	КодыНоменклатуры.Номенклатура КАК Ссылка,
    |	КодыНоменклатуры.Код КАК НовыйКод
    |ИЗ
    |	РегистрСведений.КодыНоменклатуры КАК КодыНоменклатуры";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    ОбъектНом = Выборка.Ссылка.ПолучитьОбъект();
    
    // Проверяем, нужно ли реально менять значение, чтобы лишний раз не записывать
    Если ОбъектНом.Артикул <> Выборка.НовыйКод Тогда
        ОбъектНом.Артикул = Выборка.НовыйКод;
        Попытка
            ОбъектНом.Записать();
            Сообщить("Обновлен артикул для: " + Выборка.Ссылка);
        Исключение
            Сообщить("Ошибка при записи: " + ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
КонецЦикла;

Как избежать подобных проблем в будущем?

Чтобы "магия" не возвращалась, проанализируем регламенты обслуживания вашей базы данных PostgreSQL. Рассмотрим основные рекомендации:

Подведем итог: если вы столкнулись с необъяснимым поведением данных в 1С на серверной базе, первым делом проверьте наличие физических дублей в регистрах. В 99% случаев за "магией" скрывается нарушение уникальности записей, вызванное сбоями при копировании или обслуживании СУБД.

← На главную