Почему при записи набора записей очищается весь регистр сведений и как это исправить?

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

Работа с регистрами сведений — одна из самых частых задач разработчика 1С. Однако здесь скрывается классическая «ловушка», в которую попадают даже опытные специалисты. Типичная ситуация: вы пытаетесь обновить или удалить конкретные записи по определенному условию, но в итоге обнаруживаете, что весь регистр оказался абсолютно пустым. Давайте подробно проанализируем, почему так происходит, и как правильно организовать алгоритм записи данных, чтобы избежать потери информации.

Разбор проблемной ситуации

Рассмотрим сценарий, представленный в вопросе автора. В документе перед записью табличной части необходимо обновить записи в регистре сведений псПакетыЗаявок. У регистра есть два ключевых измерения: УникальныйИдентификатор (тип Строка) и ЗаявкаНаРасходованиеДС (тип Ссылка). Логика автора была следующей:

  1. Найти существующие записи в регистре с помощью запроса по конкретному УникальныйИдентификатор.
  2. Если записи найдены, создать набор записей, установить отбор по этому идентификатору и очистить их (записать пустой набор).
  3. Затем создать новый набор записей, заполнить его новыми данными из экранной таблицы и снова записать.

Ошибка в коде автора выглядела так:


// Фрагмент, который привел к очистке всего регистра
РС = РегистрыСведений.псПакетыЗаявок.СоздатьНаборЗаписей; 
Для Каждого Строка Из ЭтаФорма.ТаблицаЗаявок Цикл 
    НоваяЗапись = РС.Добавить; 
    НоваяЗапись.ЗаявкаНаРасходованиеДС = Строка.ЗаявкаНаРасходованиеДС; 
    НоваяЗапись.УникальныйИдентификатор = Строка.УникальныйИдентификатор; 
КонецЦикла; 
РС.Записать; // Здесь происходит катастрофа

Проанализируем причину: во втором блоке кода создается абсолютно новый объект НаборЗаписей. В нем не установлен отбор. Для платформы 1С отсутствие отбора в наборе записей означает, что область действия этого набора — весь регистр целиком.

Фундаментальный механизм работы НабораЗаписей

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

  1. Удаление: Платформа находит в базе данных абсолютно все записи, которые соответствуют текущему Отбору набора записей, и безвозвратно удаляет их.
  2. Вставка: Платформа берет те строки, которые вы добавили в коллекцию набора (в память), и записывает их в таблицу.

Следовательно, если вы не установили Отбор, то на шаге 1 система видит «пустой фильтр» и удаляет все записи из таблицы регистра. Затем на шаге 2 она записывает только те несколько строк, которые вы подготовили. Результат — все остальные данные в регистре, не относящиеся к вашей задаче, будут стерты.

Правильный подход к обновлению данных

Для корректной работы мы должны придерживаться правила: любая манипуляция с набором записей должна начинаться с установки отбора. Более того, использование двух разных объектов набора (один для удаления, другой для записи) — это избыточная и опасная практика. Разберем оптимальный алгоритм.

Рассмотрим, как переписать код правильно, используя один объект НаборЗаписей:


// 1. Создаем один объект набора записей
НаборЗаписей = РегистрыСведений.псПакетыЗаявок.СоздатьНаборЗаписей();

// 2. ОБЯЗАТЕЛЬНО устанавливаем отбор. 
// Теперь набор "видит" только записи по конкретному УИД.
НаборЗаписей.Отбор.УникальныйИдентификатор.Установить(НужныйУИД);

// 3. Считываем данные из базы (опционально, если нужно что-то проверить)
// НаборЗаписей.Прочитать(); 

// 4. Очищаем текущую коллекцию в памяти (это не удаляет данные из БД сразу)
НаборЗаписей.Очистить();

// 5. Заполняем набор новыми данными
Для Каждого СтрокаТЧ Из ТаблицаЗаявок Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяЗапись, СтрокаТЧ);
    // Важно, чтобы значение в записи совпадало со значением в отборе!
    НоваяЗапись.УникальныйИдентификатор = НужныйУИД;
КонецЦикла;

// 6. Записываем. 
// Система удалит старые записи ТОЛЬКО по данному УИД и запишет новые ТОЛЬКО по нему.
НаборЗаписей.Записать(Истина);

Методы защиты от случайной очистки («Защитное программирование»)

Проанализируем ситуацию: что будет, если переменная НужныйУИД окажется пустой? Отбор установится в «пустое значение», и если в регистре есть записи с пустым идентификатором, они будут обработаны. Но если логика программы подразумевает, что пустых идентификаторов быть не может, вы все равно рискуете затереть данные.

Рекомендуется добавлять проверку перед записью:


Если ЗначениеЗаполнено(НужныйУИД) Тогда
    НаборЗаписей.Отбор.УникальныйИдентификатор.Установить(НужныйУИД);
    НаборЗаписей.Записать(Истина);
Иначе
    ВызватьИсключение "Попытка записи в регистр без указания ключевого измерения!";
КонецЕсли;

Использование Менеджера Записи

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


Менеджер = РегистрыСведений.псПакетыЗаявок.СоздатьМенеджерЗаписи();
Менеджер.УникальныйИдентификатор = ТекущийУИД;
Менеджер.ЗаявкаНаРасходованиеДС = ТекущаяЗаявка;

// Пытаемся прочитать существующую запись
Менеджер.Прочитать();

Если Менеджер.Выбран() Тогда
    // Запись существует, можем ее изменить или удалить
    Менеджер.Удалить(); 
КонецЕсли;

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

Важные замечания по производительности и типам данных

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

Совет: Всегда используйте специализированные типы данных (СправочникСсылка, ДокументСсылка, УникальныйИдентификатор, Число, Дата) для измерений регистра вместо строк, если это возможно.

Блокировки данных

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

Итоги

Мы выяснили, что «исчезновение» данных из регистра при записи набора — это штатное поведение платформы 1С при отсутствии установленного отбора. Чтобы ваша работа с регистрами была безопасной и эффективной, следуйте этим правилам:

  1. Никогда не вызывайте Записать() у набора записей, не убедившись, что отбор установлен по всем необходимым измерениям.
  2. Используйте один и тот же объект набора для очистки старых и добавления новых данных в рамках одного контекста.
  3. Всегда проверяйте параметры отбора на заполненность перед выполнением операций записи.
  4. Для работы с единичными записями отдавайте предпочтение МенеджеруЗаписи.
  5. Старайтесь избегать строковых типов для ключевых полей (измерений) регистра.

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

← На главную