Как исправить ошибку «Запись с такими ключевыми полями существует!» при программной работе с регистром сведений?

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

При разработке в среде 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда попытка записать данные в регистр сведений (РС) прерывается ошибкой: «Запись с такими ключевыми полями существует!». На первый взгляд это кажется странным, особенно если в методе Записать() установлен параметр Замещать = Истина. Казалось бы, система должна просто заменить старые данные новыми, но вместо этого выполнение кода прерывается.

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

Разбор основной причины: почему Замещать = Истина не спасает?

Рассмотрим ситуацию, в которую попал автор темы. Код выглядел примерно так:


Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
Набор.Прочитать(); // Загружаем существующие записи из базы в память

Для сч = 1 По КоличествоР Цикл
    Запись = Набор.Добавить(); // Добавляем новую запись
    Запись.Измерение1 = Значение;
    // ... заполнение остальных полей
КонецЦикла;

Набор.Записать(Истина); // ОШИБКА!

Давайте проанализируем ситуацию. Когда мы вызываем Набор.Прочитать(), объект Набор в оперативной памяти заполняется строками, которые уже есть в базе данных. Затем, используя цикл и метод Добавить(), мы создаем в этом же объекте новые строки. Если значения ключевых полей (измерений) в новых строках совпадают с теми, что были прочитаны из базы, в объекте Набор образуется дубликат.

Важный технический нюанс: параметр Замещать = Истина в методе Записать() означает, что перед записью система удалит из базы данных все записи, соответствующие установленному отбору набора. Однако проверка на уникальность ключевых полей происходит внутри самого набора еще до того, как данные окончательно попадут в таблицу СУБД. Если в вашем наборе в памяти есть две строки с одинаковыми измерениями — 1С выдаст ошибку, так как это нарушает базовый принцип уникальности записей регистра сведений.

Способ 1: Использование правильных отборов (Best Practice)

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

Проанализируем правильный алгоритм:

  1. Создаем пустой набор записей.
  2. Устанавливаем Отбор по тем измерениям, которые мы хотим «перезаписать».
  3. Заполняем набор новыми данными (метод Прочитать() вызывать НЕ нужно, если нам не важны старые данные).
  4. Вызываем Записать(Истина).

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


Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
// Устанавливаем фильтр — работаем только с одной позицией
Набор.Отбор.Номенклатура.Установить(ТекущаяНоменклатура);
Набор.Отбор.ВидЦены.Установить(ТекущийВидЦены);

// Нам НЕ нужно читать старые записи, если мы их полностью заменяем
НоваяЗапись = Набор.Добавить();
НоваяЗапись.Номенклатура = ТекущаяНоменклатура;
НоваяЗапись.ВидЦены = ТекущийВидЦены;
НоваяЗапись.Цена = 1500;

// Система удалит старую цену для этой номенклатуры и запишет новую
Набор.Записать(Истина);

Способ 2: Очистка набора после чтения

Если по какой-то причине вам все же необходимо сначала прочитать данные (например, чтобы проанализировать их перед принятием решения о перезаписи), не забывайте очищать коллекцию в памяти перед добавлением новых данных, либо модифицировать уже существующие строки вместо добавления новых.

Посмотрим на пример с очисткой:


Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
Набор.Отбор.Измерение.Установить(Значение);
Набор.Прочитать();

// Если решили, что данные нужно полностью обновить:
Набор.Очистить(); 

Запись = Набор.Добавить();
// ... заполнение ...
Набор.Записать(Истина);

Альтернативно, мы можем найти нужную строку в прочитанном наборе и изменить в ней Ресурсы, не трогая Измерения. В этом случае состав ключевых полей не изменится, и дубля не возникнет.

Способ 3: Использование Менеджера записи

Если ваша задача — обновить или создать всего одну конкретную запись, использование НабораЗаписей может быть избыточным. Для точечной работы идеально подходит МенеджерЗаписи. Рассмотрим его преимущества:

  1. Он работает строго с одной комбинацией ключевых полей.
  2. Метод Прочитать() у менеджера записи заполняет значения ресурсов, если такая запись уже есть.
  3. Метод Записать() сам определит: обновить существующую строку или создать новую.

Менеджер = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
Менеджер.Измерение1 = Значение1;
Менеджер.Измерение2 = Значение2;

Менеджер.Прочитать(); // Пытаемся найти запись в базе
Менеджер.Ресурс1 = НовоеЗначение; // Меняем данные

Менеджер.Записать(); // Система сама разберется: UPDATE или INSERT

Особенности периодических регистров

При работе с периодическими регистрами сведений в состав уникального ключа добавляется поле Период. Выясним, как это влияет на возникновение ошибки. Если вы записываете две строки с абсолютно одинаковыми измерениями, но разным временем (даже с разницей в одну секунду), ошибки уникальности не будет.

Однако часто программисты забывают обнулять или устанавливать время, из-за чего в наборе оказываются записи, например, на начало дня (00:00:00). Если таких записей две — вы получите обсуждаемую ошибку. Всегда проверяйте, какой Период устанавливается для новых записей в коде.

Технические нюансы: Блокировки и режим загрузки

Проанализируем более глубокие аспекты работы платформы 1С:

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

2. Свойство ОбменДанными.Загрузка. Если вы выполняете массовую загрузку данных и уверены в их корректности, можно использовать Набор.ОбменДанными.Загрузка = Истина. Это отключит проверки в модуле набора записей и может ускорить процесс. Но внимание: проверка на уровне СУБД (SQL) всё равно останется! Если в наборе будут дубли по ключевым полям, база данных не примет эти данные, и вы всё равно увидите ошибку.

3. Проверка уникальности в коде. Перед тем как добавлять данные в набор из внешней таблицы (например, из Excel), рекомендуется выполнить предварительную проверку. Самый простой способ — загрузить данные в ТаблицуЗначений и выполнить метод Свернуть() по колонкам, которые являются измерениями регистра. Это гарантированно уберет дубли еще до начала работы с объектами 1С.

Выяснив все эти детали, мы понимаем: ошибка «Запись с такими ключевыми полями существует!» — это не сбой программы, а защитный механизм платформы, предотвращающий появление логического мусора в базе данных. Используйте Отбор, избегайте ненужных Прочитать() внутри циклов записи и всегда контролируйте уникальность ключей в памяти вашего приложения.

← На главную