В процессе работы с высоконагруженными системами на базе 1С, такими как ERP Управление предприятием или Комплексная автоматизация, программисты часто сталкиваются со специфической системной ошибкой: «Набор записей, принадлежащий менеджеру записи должен содержать ровно 1 запись». Данная ситуация является критической, так как она блокирует работу кадровых документов, проведение настроек или открытие важных форм. Разберем подробно, почему возникает этот сбой и как его устранить различными методами.
Для начала проанализируем архитектуру регистров сведений. В 1С существуют регистры, которые не имеют ни одного измерения — в них присутствуют только ресурсы и реквизиты. С точки зрения логики платформы, такой регистр может содержать только одну строку. Для обращения к этой строке используется объект РегистрСведенийМенеджерЗаписи.
Ошибка возникает в тот момент, когда из-за программного сбоя, некорректного обмена данными или прямого вмешательства в базу данных на уровне СУБД в таком регистре физически появляется две и более записи. Платформа «теряется»: менеджер записи пытается прочитать «единственную» строку, находит несколько и выдает исключение, так как не может определить, какую из них считать актуальной. Обычное Тестирование и исправление (ТИИ) зачастую не видит в этом ошибки, так как индексы уникальности в таких таблицах могут отсутствовать или работать иначе.
Если в тексте ошибки не указано имя регистра (например, она возникает при открытии формы документа), нам необходимо его вычислить. Рассмотрим основные способы:
EXCP. Там будет зафиксирован SQL-запрос, в котором фигурирует имя таблицы (например, _InfoReg1234).НастройкиУчетаВремени, ПараметрыРаботыПрограммы или служебных регистрах настроек пользователей.Простое удаление через менеджер записи (СоздатьМенеджерЗаписи().Удалить()) не сработает, так как менеджер сам по себе инициирует проверку на количество записей. Выясним, как обойти это ограничение. Рассмотрим самый эффективный метод — использование объекта НаборЗаписей с принудительным режимом загрузки.
Выполним следующий код во внешней обработке:
// Выбираем нужный регистр
НЗ = РегистрыСведений.НастройкиУчетаВремени.СоздатьНаборЗаписей();
// Устанавливаем режим загрузки, который отключает стандартные проверки платформы
НЗ.ОбменДанными.Загрузка = Истина;
// Записываем пустой набор, чтобы полностью очистить регистр
НЗ.Записать();
Установка свойства ОбменДанными.Загрузка = Истина является ключевым моментом. Это сообщает платформе, что данные записываются «как есть», без выполнения прикладных проверок и логики подписок на события, которые могли бы вызвать повторное появление ошибки.
Если мы подозреваем, что проблема носит массовый характер или хотим создать инструмент для профилактики, проанализируем ситуацию программно по всем метаданным. Посмотрим на пример кода, который находит регистры без измерений, проверяет их на наличие «дублей» и схлопывает лишние записи:
Для Каждого Рег Из Метаданные.РегистрыСведений Цикл
// Проверяем только регистры без измерений
Если Рег.Измерения.Количество() = 0 Тогда
НаборЗаписей = РегистрыСведений[Рег.Имя].СоздатьНаборЗаписей();
НаборЗаписей.Прочитать();
// Если записей больше одной — это наша ошибка
Если НаборЗаписей.Количество() > 1 Тогда
Сообщить("Обнаружены дубли в регистре: " + Рег.Имя);
// Выгружаем записи в таблицу значений для сворачивания
ТЗ = НаборЗаписей.Выгрузить();
// Формируем список колонок для свертки (все ресурсы и реквизиты)
КолонкиСвертки = "";
Для Каждого Колонка Из ТЗ.Колонки Цикл
КолонкиСвертки = КолонкиСвертки + Колонка.Имя + ",";
КонецЦикла;
КолонкиСвертки = Лев(КолонкиСвертки, СтрДлина(КолонкиСвертки) - 1);
// Сворачиваем таблицу, оставляя только уникальную строку
ТЗ.Свернуть(КолонкиСвертки);
// Загружаем обратно и записываем в режиме обмена
НаборЗаписей.Загрузить(ТЗ);
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать(Истина);
Сообщить("Регистр " + Рег.Имя + " успешно очищен от дублей.");
КонецЕсли;
КонецЕсли;
КонецЦикла;
В исключительных случаях, когда программные методы 1С не помогают (что случается крайне редко при использовании ОбменДанными.Загрузка), задачу можно решить на уровне СУБД. Разберем этот алгоритм:
ПолучитьСтруктуруХраненияБазыДанных().TRUNCATE TABLE _InfoRegXXXX (где XXXX — номер таблицы вашего регистра).Важно: после очистки таблицы через SQL необходимо перезапустить сеанс 1С или очистить кэш, чтобы платформа увидела изменения. Помните, что TRUNCATE полностью удаляет данные, поэтому после этого настройки в программе (например, константы учета времени) нужно будет пересохранить вручную.
Проанализируем, как не допустить появления этой ошибки в будущем. Основная причина задвоения записей в коде — это использование метода Записать() у менеджера записи без предварительной блокировки или проверки существования данных.
Правильный подход при записи в такие регистры:
МенеджерЗаписи.Прочитать() перед изменением ресурсов.БлокировкаДанных, чтобы исключить ситуацию «гонки», когда два пользователя одновременно пытаются записать настройки в один и тот же регистр.НаборЗаписей, так как он более предсказуемо ведет себя при работе с коллекцией строк.