Рассмотрим весьма интересную и нетривиальную ситуацию, с которой иногда сталкиваются разработчики и администраторы баз данных 1С. В периодическом регистре сведений, подчиненном регистратору (например, ЦеныНоменклатуры в УТ 10 с периодичностью «в пределах дня»), обнаруживаются две записи с абсолютно одинаковым периодом и одинаковыми значениями всех измерений. При этом регистраторы у этих записей разные — это два разных документа одного типа.
При попытке перепровести любой из этих документов система выдает ошибку о неуникальности данных. Установка флага ОбменДанными.Загрузка = Истина не помогает обойти проверку. Давайте подробно разберем, как такая ситуация вообще могла возникнуть в базе, проанализируем архитектуру хранения данных и выясним способы решения проблемы.
Чтобы понять причину, проанализируем то, как платформа 1С хранит данные регистра сведений на уровне системы управления базами данных (СУБД), будь то MS SQL Server или PostgreSQL.
Для регистра сведений, подчиненного регистратору, на физическом уровне базы данных первичный ключ таблицы не состоит из измерений и периода. Физическим ключом выступает комбинация полей: Ссылка на регистратор + Номер строки. Технически, таблица в СУБД вполне способна хранить две записи с одинаковыми измерениями и периодом от разных регистраторов, так как их физические первичные ключи будут уникальны.
Уникальность же по набору Период + Измерения является логической. Платформа 1С обеспечивает ее путем создания отдельного уникального индекса в самой базе данных. Появление дублей означает, что данные попали в таблицу в тот момент, когда этот индекс не работал, отсутствовал, либо структура регистра была иной.
Разберем основные причины, из-за которых логическая уникальность записей могла быть нарушена:
Часто программисты пытаются обойти проверки уникальности с помощью конструкции ОбменДанными.Загрузка = Истина. Посмотрим, почему в данной ситуации это бессмысленно.
Этот флаг отключает только проверки на уровне программного кода 1С — подписки на события, процедуры ПередЗаписью, ПриЗаписи и другие встроенные проверки модулей. Однако он не отменяет ограничений на уровне СУБД. При записи набора СУБД «видит» конфликт по уникальному индексу (если он существует) и возвращает аппаратную ошибку базы данных, которую платформа 1С проигнорировать не может.
Обычными пользовательскими средствами (отмена проведения, перепроведение) проблему не решить: при отмене проведения первого документа, второй всё равно не перепроведется из-за проверки СУБД или платформы. Изменить метаданные (например, вернуть периодичность до секунды) платформа тоже не даст, так как при реструктуризации будет вызвана ошибка.
Самый безопасный и эффективный метод — программное удаление одного из проблемных наборов записей. Напишем небольшую внешнюю обработку (или выполним код в консоли кода), которая удалит записи одного из регистраторов-дублей.
Рассмотрим пример кода, который очищает набор записей по конкретному документу-регистратору:
// Получаем проблемный документ-регистратор
ДокументСсылка = Документы.УстановкаЦенНоменклатуры.НайтиПоНомеру("000000001", ТекущаяДата());
Если ЗначениеЗаполнено(ДокументСсылка) Тогда
// Создаем набор записей проблемного регистра
НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
// Устанавливаем отбор по нашему документу
НаборЗаписей.Отбор.Регистратор.Установить(ДокументСсылка);
// Включаем режим загрузки на всякий случай, чтобы обойти программные проверки
НаборЗаписей.ОбменДанными.Загрузка = Истина;
// Выполняем запись пустого набора (фактически - удаление записей из БД)
Попытка
НаборЗаписей.Записать(Истина); // Истина = Замещать существующие
Сообщить("Записи регистра успешно очищены.");
Исключение
Сообщить("Ошибка при очистке набора записей: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
После выполнения данного скрипта физические строки одного из регистраторов будут удалены из базы. Конфликт логической уникальности исчезнет, и вы сможете штатно перепровести документы в нужном вам порядке. Если же ошибка остается даже при программной очистке, то это сигнализирует о серьезных повреждениях таблицы на уровне СУБД. В таком крайнем случае потребуется точечное удаление строки-дубля через прямые SQL-запросы (для серверной базы) или утилиту Tool1CD (для файловой базы), после чего обязательно следует выполнить полное Тестирование и исправление БД.