При работе с регистрами сведений в среде 1С:Предприятие 8 разработчики часто сталкиваются с ситуацией, когда при записи данных система выдает ошибку: «Запись с такими ключевыми полями существует!». Эта ситуация вводит в ступор, особенно когда визуальный осмотр регистра или запрос к базе данных показывают полное отсутствие подобных записей. В этой статье мы подробно разберем, почему «пустой» регистр может выдавать такую ошибку, научимся диагностировать проблему и рассмотрим программные способы её решения.
Первое, что мы должны уяснить: ошибка неуникальности при записи набора записей (РегистрСведенийНаборЗаписей) далеко не всегда означает наличие дублей в самой базе данных. Рассмотрим механизм записи: когда мы вызываем метод Записать(), платформа 1С выполняет проверку уникальности внутри самого передаваемого набора. Если в коде мы добавили в набор две строки с одинаковыми значениями измерений (и одинаковым периодом для периодических регистров), система не сможет записать такой набор, так как он нарушает базовый принцип уникальности регистра.
Проанализируем типичную ситуацию: регистр подчинен регистратору. Мы создаем набор, устанавливаем отбор по документу-регистратору. Даже если набор изначально пуст (НаборЗаписей.Прочитать() возвращает 0 строк), добавление в него дублирующихся строк внутри цикла обхода данных документа приведет к фатальной ошибке при попытке фиксации транзакции.
Как показывает практика, в 90% случаев проблема кроется в данных объекта-источника (например, табличной части документа). Если в документе присутствуют две одинаковые строки (одна и та же номенклатура, склад, характеристика), то при проведении они превратятся в две одинаковые записи в наборе.
Рассмотрим пример кода, который часто становится источником проблем:
Процедура РегистраторыНоменклатуры(МассивПроводок, ДатаДокумента, Ссылка) Экспорт
НаборЗаписей = РегистрыСведений.РегистраторыДвижений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Ссылка);
Для Каждого Проводка Из МассивПроводок Цикл
Запись = НаборЗаписей.Добавить();
Запись.Период = ДатаДокумента;
Запись.Номенклатура = Проводка.Номенклатура;
Запись.Штрихкод = Проводка.Штрихкод;
Запись.Регистратор = Ссылка;
КонецЦикла;
НаборЗаписей.Записать();
КонецПроцедуры
В данном примере, если в параметре МассивПроводок окажутся два элемента с идентичной номенклатурой и штрихкодом, метод НаборЗаписей.Записать() вызовет исключение. Выясним причину: платформа проверяет уникальность по комбинации измерений. Если в регистре измерениями являются Номенклатура и Штрихкод, то две записи с одинаковой парой значений недопустимы в рамках одного набора.
Если мы не видим дублей в коде, нам поможет отладчик. Разберем по шагам процесс диагностики:
НаборЗаписей.Записать().НаборЗаписей.Количество().НаборЗаписей.Выгрузить(). Полученную таблицу можно просмотреть визуально или сохранить в файл для детального изучения.Самый надежный способ избежать ошибки, если бизнес-логика допускает объединение одинаковых строк — это принудительная свертка данных перед загрузкой в набор. Рассмотрим, как модифицировать процедуру записи, чтобы она стала «неубиваемой»:
НаборЗаписей = РегистрыСведений.РегистраторыДвижений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Ссылка);
// Сначала подготавливаем таблицу данных
ТЗ_Движений = Новый ТаблицаЗначений;
ТЗ_Движений.Колонки.Добавить("Период");
ТЗ_Движений.Колонки.Добавить("Номенклатура");
ТЗ_Движений.Колонки.Добавить("Штрихкод");
ТЗ_Движений.Колонки.Добавить("Регистратор");
Для Каждого Проводка Из МассивПроводок Цикл
НоваяСтрока = ТЗ_Движений.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Проводка);
НоваяСтрока.Период = ДатаДокумента;
НоваяСтрока.Регистратор = Ссылка;
КонецЦикла;
// ВАЖНО: Сворачиваем по всем измерениям и периоду
ТЗ_Движений.Свернуть("Период, Номенклатура, Штрихкод, Регистратор");
// Загружаем очищенные данные в набор
НаборЗаписей.Загрузить(ТЗ_Движений);
НаборЗаписей.Записать();
Обратите внимание: при использовании метода Свернуть() крайне важно указывать в первом параметре только те поля, которые являются измерениями (или периодом). Если вы случайно добавите в список группировки поле НомерСтроки (которое у каждой строки разное), свертка не произойдет, и дубли останутся.
Бывают случаи, когда программный код идеален, дублей в наборе нет, а ошибка все равно возникает. Проанализируем более глубокие причины:
1. Невидимые символы в строковых измерениях. Если одно из измерений — строка, в него могут попасть непечатные символы (неразрывный пробел, перевод строки). В Excel или консоли запросов записи "Товар" и "Товар " выглядят одинаково, но для SQL и платформы 1С это разные значения. Однако при определенных настройках кодировки базы данных (Case Insensitivity) или при попытке записи система может посчитать их конфликтующими.
2. Проблемы с индексами на уровне SQL Server. В редких случаях (особенно при сбоях питания или аварийном завершении работы сервера) индексы таблицы регистра могут «расползтись». В такой ситуации 1С не видит запись, а SQL при попытке вставки выдает ошибку нарушения уникального ключа. Решение — выполнение реиндексации через конфигуратор (Тестирование и исправление) или средствами СУБД.
3. Миллисекунды в периоде. Платформа 1С работает с точностью до секунды, но в некоторых интеграционных сценариях в поле Период могут попасть значения с миллисекундами. При сравнении в 1С они могут выглядеть одинаковыми, но при записи вызывать коллизии. Всегда полезно нормализовать дату с помощью функции КонецСекунды() или НачалоСекунды().
Чтобы минимизировать риск возникновения проблем с уникальностью записей, придерживайтесь следующих правил:
ПередЗаписью или ПриЗаписи регистра. Другой разработчик мог добавить туда логику, которая модифицирует ваш набор записей, добавляя в него новые строки.ПередЗаписью, в котором будет происходить финальная проверка или свертка набора.НаборЗаписей.Записать(Истина) полностью замещает записи по установленному отбору. Если отбор установлен неверно, вы можете случайно попытаться перезаписать чужие данные.Подведем итог. Ошибка неуникальности в «пустом» регистре — это почти всегда результат наличия дублей в оперативной памяти (в самом объекте НаборЗаписей). Тщательный аудит исходных данных и использование метода Свернуть() перед записью — это стандарт «золотого сечения» при разработке механизмов проведения документов в 1С.