В процессе работы с конфигурациями на платформе 1С:Предприятие 8.3 (такими как КА 2, ERP 2 или ЗУП 3) технические специалисты иногда сталкиваются со странной и пугающей ошибкой: «Запись не верна! Набор записей, принадлежащий менеджеру записи, должен содержать ровно 1 запись». Эта проблема блокирует работу с документами (например, «Прием на работу») или препятствует открытию форм настроек. В этой статье мы подробно разберем, почему возникает эта ситуация, как проанализировать проблемные регистры и какими способами можно устранить дубли в базе данных.
Прежде чем переходить к лечению, разберем этиологию «болезни». Обычно ошибка возникает в непериодических регистрах сведений, в которых отсутствуют измерения. Такие регистры в типовых конфигурациях 1С используются для хранения глобальных настроек или констант (например, НастройкиШтатногоРасписания, НастройкиУчетаВремени). Логика платформы подразумевает, что в таком регистре может быть только одна-единственная запись.
Рассмотрим причины возникновения дублей:
UPDATE) платформа выполняла вставку новой (INSERT).Ситуация осложняется тем, что стандартными средствами 1С (через интерфейс) удалить такие записи невозможно, так как платформа при любой попытке обращения к регистру выбрасывает исключение, видя, что записей больше одной. В штатных ситуациях для исправления данных применяется универсальная работа с регистрами для управляемых форм.
Если нам удалось программно прочитать набор записей, мы можем попытаться удалить одну лишнюю строку. Этот метод сработает, если метод Прочитать() не вызывает ошибку немедленно. Проанализируем пример кода, который позволяет точечно удалить дубль:
НаборЗаписей = РегистрыСведений.НастройкиУчетаВремени.СоздатьНаборЗаписей();
НаборЗаписей.Прочитать();
// Проверяем, что записей действительно больше одной
Если НаборЗаписей.Количество() > 1 Тогда
// Удаляем первую запись из набора (индекс 0)
НаборЗаписей.Удалить(0);
// Пытаемся записать набор обратно.
// Использование ОбменДанными.Загрузка позволяет обойти некоторые проверки.
НаборЗаписей.ОбменДанными.Загрузка = Истина;
Попытка
НаборЗаписей.Записать();
Сообщить("Лишняя запись успешно удалена.");
Исключение
Сообщить("Ошибка при записи набора: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Важно помнить: этот способ поможет, только если в регистре всего 2 записи. Если их больше, или если платформа блокирует даже чтение, переходим к следующему варианту.
Если метод Прочитать() вызывает ошибку «Набор записей должен содержать ровно 1 запись», значит, мы не можем загрузить данные в память для манипуляций. В этом случае выясним причину: платформа блокирует чтение, так как нарушена логика «одна запись на менеджер». Решением будет полная очистка таблицы регистра и создание записи заново.
Разберем алгоритм действий:
Прочитать().ОбменДанными.Загрузка = Истина.DELETE на уровне СУБД для всех записей этого регистра.
Набор = РегистрыСведений.НастройкиШтатногоРасписания.СоздатьНаборЗаписей();
// Набор.Прочитать(); <-- ЭТОТ МЕТОД НЕ ВЫЗЫВАЕМ, он вызывает ошибку
Набор.ОбменДанными.Загрузка = Истина;
Набор.Записать(); // Теперь таблица в СУБД абсолютно пуста
После выполнения этого кода вам нужно зайти в пользовательский интерфейс программы (Настройки — Параметры учета) и заново установить нужные флажки. При сохранении настроек 1С создаст одну корректную запись.
Часто бывает так, что после технического сбоя или некорректного обновления конфигурации «плывут» сразу несколько регистров настроек. Чтобы не перебирать их вручную, мы можем воспользоваться универсальным скриптом, который проанализирует все регистры сведений без измерений.
Рассмотрим код для массовой обработки:
Для Каждого Рег Из Метаданные.РегистрыСведений Цикл
// Нас интересуют только непериодические регистры без измерений
Если Рег.Периодичность = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический
И Рег.Измерения.Количество() = 0 Тогда
НаборЗаписей = РегистрыСведений[Рег.Имя].СоздатьНаборЗаписей();
Попытка
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() > 1 Тогда
Сообщить("Найден дубль в регистре: " + Рег.Имя);
// Оставляем только одну запись
Пока НаборЗаписей.Количество() > 1 Цикл
НаборЗаписей.Удалить(0);
КонецЦикла;
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать(Истина);
Сообщить("Регистр " + Рег.Имя + " успешно исправлен.");
КонецЕсли;
Исключение
// Если прочитать не удалось из-за ошибки платформы, очищаем принудительно
Сообщить("Критическая ошибка в регистре " + Рег.Имя + ". Очищаем полностью...");
НаборОчистки = РегистрыСведений[Рег.Имя].СоздатьНаборЗаписей();
НаборОчистки.ОбменДанными.Загрузка = Истина;
НаборОчистки.Записать();
КонецПопытки;
КонецЕсли;
КонецЦикла;
Если программные методы из 1С не помогают (что бывает крайне редко, но возможно при сильном повреждении таблиц), задачу можно решить прямым SQL-запросом через pgAdmin или консоль psql.
Внимание: любые действия напрямую в базе данных требуют наличия свежей резервной копии!
Сначала выясним имя таблицы в СУБД. Это можно сделать с помощью функции ПолучитьСтруктуруХраненияБазыДанных(). Допустим, наш регистр называется _inforg1234. В PostgreSQL есть скрытое системное поле ctid, которое является физическим адресом строки. Мы можем использовать его, чтобы удалить одну из двух идентичных строк.
Выполним следующий запрос:
DELETE FROM _inforg1234
WHERE ctid NOT IN (
SELECT min(ctid)
FROM _inforg1234
);
Этот запрос оставит в таблице только ту запись, которая была вставлена первой (с минимальным адресом ctid), и удалит все ее полные копии. Если регистр должен быть гарантированно пустым, можно использовать команду TRUNCATE TABLE _inforg1234;, которая мгновенно очищает таблицу.
Чтобы избежать повторения ситуации, проанализируем следующие рекомендации:
Мы проанализировали проблему дублей в регистрах сведений и рассмотрели четыре различных способа ее решения — от простого программного удаления до прямого вмешательства в СУБД. Выбор метода зависит от тяжести ситуации, но чаще всего полная очистка набора записей с установкой ОбменДанными.Загрузка = Истина является самым быстрым и безопасным выходом.