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

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

Рассмотрим весьма интересную и нетривиальную ситуацию, с которой иногда сталкиваются разработчики и администраторы баз данных 1С. В периодическом регистре сведений, подчиненном регистратору (например, ЦеныНоменклатуры в УТ 10 с периодичностью «в пределах дня»), обнаруживаются две записи с абсолютно одинаковым периодом и одинаковыми значениями всех измерений. При этом регистраторы у этих записей разные — это два разных документа одного типа.

При попытке перепровести любой из этих документов система выдает ошибку о неуникальности данных. Установка флага ОбменДанными.Загрузка = Истина не помогает обойти проверку. Давайте подробно разберем, как такая ситуация вообще могла возникнуть в базе, проанализируем архитектуру хранения данных и выясним способы решения проблемы.

Архитектура хранения данных: Физический и логический ключи

Чтобы понять причину, проанализируем то, как платформа 1С хранит данные регистра сведений на уровне системы управления базами данных (СУБД), будь то MS SQL Server или PostgreSQL.

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

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

Почему появляются дублирующиеся записи?

Разберем основные причины, из-за которых логическая уникальность записей могла быть нарушена:

  1. Изменение периодичности регистра. Это самая частая причина. Изначально регистр мог иметь периодичность «в пределах секунды» или «по позиции регистратора». В этот момент два разных документа сделали записи одним и тем же днем, и это было корректно. Позже периодичность изменили на «в пределах дня». При обновлении структуры 1С могла проигнорировать конфликт или индекс не перестроился корректно.
  2. Удаление измерения. Если в регистре было дополнительное измерение, по которому записи различались, то в момент его удаления в конфигураторе эти записи стали логическими дублями.
  3. Сбой в процессе реструктуризации. При обновлениях конфигурации 1С пересоздает таблицы и индексы. Если в этот момент произошел аппаратный сбой, уникальный индекс мог быть временно удален базой. Если записи-дубли попали в базу именно тогда, последующее создание индекса прошло с ошибкой, и индекс просто не был создан.
  4. Прямая запись через SQL (Direct SQL) или внешние инструменты. Данные могли загрузить напрямую в таблицы СУБД скриптом, в обход сервера приложений 1С, который и следит за корректностью бизнес-логики.
  5. Последствия утилиты Тестирование и исправление (ТИИ). При наличии «битых» ссылок или поврежденных таблиц ТИИ могло сгенерировать фантомные записи или привязать «потерянные» записи к документам-пустышкам.

Почему не помогает ОбменДанными.Загрузка = Истина?

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

Этот флаг отключает только проверки на уровне программного кода 1С — подписки на события, процедуры ПередЗаписью, ПриЗаписи и другие встроенные проверки модулей. Однако он не отменяет ограничений на уровне СУБД. При записи набора СУБД «видит» конфликт по уникальному индексу (если он существует) и возвращает аппаратную ошибку базы данных, которую платформа 1С проигнорировать не может.

Как решить проблему и удалить дубли

Обычными пользовательскими средствами (отмена проведения, перепроведение) проблему не решить: при отмене проведения первого документа, второй всё равно не перепроведется из-за проверки СУБД или платформы. Изменить метаданные (например, вернуть периодичность до секунды) платформа тоже не даст, так как при реструктуризации будет вызвана ошибка.

Самый безопасный и эффективный метод — программное удаление одного из проблемных наборов записей. Напишем небольшую внешнюю обработку (или выполним код в консоли кода), которая удалит записи одного из регистраторов-дублей.

Рассмотрим пример кода, который очищает набор записей по конкретному документу-регистратору:


// Получаем проблемный документ-регистратор
ДокументСсылка = Документы.УстановкаЦенНоменклатуры.НайтиПоНомеру("000000001", ТекущаяДата());

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

После выполнения данного скрипта физические строки одного из регистраторов будут удалены из базы. Конфликт логической уникальности исчезнет, и вы сможете штатно перепровести документы в нужном вам порядке. Если же ошибка остается даже при программной очистке, то это сигнализирует о серьезных повреждениях таблицы на уровне СУБД. В таком крайнем случае потребуется точечное удаление строки-дубля через прямые SQL-запросы (для серверной базы) или утилиту Tool1CD (для файловой базы), после чего обязательно следует выполнить полное Тестирование и исправление БД.

← На главную