При разработке алгоритмов пакетной обработки данных в платформе 1С:Предприятие часто возникает задача отобрать элементы справочника по значению конкретного реквизита и заменить его на новое. Однако при попытке передать структуру отбора в стандартный метод менеджера справочника Выбрать() разработчики нередко сталкиваются с ошибкой выполнения: «Недопустимое значение параметра (параметр номер '3')».
Разберем детально, почему возникает данное исключение, какие строгие ограничения накладывает платформа на параметры метода Выбрать(), почему временное включение индексации в метаданных является опасным подходом, а также рассмотрим эталонные способы выборки и перезаписи данных в 1С.
Проанализируем синтаксис метода менеджера справочника Выбрать(), описанный в Синтаксис-помощнике. Метод принимает следующие параметры:
Параметр номер 3 (Отбор) имеет жесткие платформенные ограничения:
Код, Наименование, а также реквизиты объекта, для которых в свойствах объекта метаданных в конфигураторе свойство «Индексировать» явно переведено в значение Индексировать или Индексировать с доп. упорядоч..Если реквизит, указанный в структуре (например, ITP_Организация), не является индексируемым полем, платформа 1С аварийно завершает выполнение метода и сообщает о недопустимом значении третьего параметра.
Распространенным заблуждением является временное изменение свойства реквизита на Индексировать с последующим обновлением конфигурации базы данных и возвратом свойства обратно после работы скрипта. Выясним, почему так делать категорически не рекомендуется:
Посмотрим на фрагмент кода, где производится обход выборки:
Пока Выборка.Следующий() Цикл
СпрОбъект = Выборка.ПолучитьОбъект();
СпрОбъект.ITP_Организация = НоваяОрг;
КонецЦикла;
В этом коде допущена критическая ошибка: отсутствует вызов метода СпрОбъект.Записать(). Без явного вызова метода Записать() объект модифицируется только в оперативной памяти и сразу же уничтожается сборщиком мусора при переходе к следующей итерации цикла. Изменения в базу данных зафиксированы не будут.
Наиболее производительным, надежным и гибким способом работы с данными в 1С является использование языка запросов. Запрос позволяет накладывать условия на любые поля (независимо от их индексации), фильтровать по нескольким условиям и получать исключительно поле ссылки, экономя оперативную память сервера.
Рассмотрим по шагам правильную реализацию пакетного обновления реквизита:
СтараяОрг = Справочники.Организации.НайтиПоКоду("Гл0000016");
НоваяОрг = Справочники.Организации.НайтиПоКоду("Гл0000028");
Если Не ЗначениеЗаполнено(СтараяОрг) Или Не ЗначениеЗаполнено(НоваяОрг) Тогда
ВызватьИсключение "Не удалось найти одну из организаций по указанному коду!";
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ITP_Организация = &СтараяОрганизация
| И НЕ Контрагенты.ЭтоГруппа";
Запрос.УстановитьПараметр("СтараяОрганизация", СтараяОрг);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Счетчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
Попытка
СпрОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Отключаем проверки перед записью для ускорения и исключения блокировок
СпрОбъект.ОбменДанными.Загрузка = Истина;
СпрОбъект.ITP_Организация = НоваяОрг;
СпрОбъект.Записать();
Счетчик = Счетчик + 1;
// Фиксируем транзакцию порциями по 1000 элементов
Если Счетчик % 1000 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
Исключение
ОтменитьТранзакцию();
ВызватьИсключение "Ошибка при записи объекта: " + ОписаниеОшибки();
КонецПопытки;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
Проанализируем ключевые оптимизации в приведенном коде:
ITP_Организация без необходимости перенастройки метаданных. Также добавлено условие НЕ Контрагенты.ЭтоГруппа, чтобы избежать попытки записи реквизита в группы справочника, если они не поддерживают данный реквизит.СпрОбъект.ОбменДанными.Загрузка = Истина отключает выполнение избыточных проверок в модуле объекта и подписках на события, а также предотвращает лишнюю регистрацию изменений в планах обмена.НачатьТранзакцию() / ЗафиксироватьТранзакцию()) пачками по 500–1000 элементов ускоряет обработку в десятки раз.Попытка ... Исключение защищает целостность данных и корректно отменяет активную транзакцию при возникновении непредвиденных ошибок записи.Если замену реквизита требуется выполнить в рабочей базе без написания программного кода, воспользуемся стандартной обработкой, встроенной в состав большинства типовых конфигураций («1С:Библиотека стандартных подсистем»):
Групповое изменение реквизитов).Контрагенты.Организация (или ITP_Организация) равен старому значению.Изменять в транзакции или Прерывать по ошибке при необходимости.