Часто перед разработчиком 1С встает задача: сначала нужно выбрать определенные записи из регистра сведений по сложным условиям с помощью запроса, а затем внести в них изменения. Сам по себе запрос только читает данные, но не модифицирует их. В этой статье мы подробно разберем, какие стандартные и надежные механизмы платформы 1С существуют для изменения записей регистра сведений, и какой из них выбрать в вашей ситуации.
Проанализируем основные подходы: использование Менеджера записи для точечных изменений, Набора записей для групповых операций и комбинированный метод обработки результата запроса, который сочетает гибкость и безопасность.
Этот метод является предпочтительным и наиболее безопасным для изменения одной конкретной записи. Его логика основана на принципе "Прочитать-Изменить-Записать", что гарантирует целостность данных и предотвращает случайное создание дублей. Он идеально подходит, когда вам нужно изменить одну запись, ключ которой вам точно известен.
Рассмотрим по шагам, как работать с менеджером записи:
СоздатьМенеджерЗаписи().Период для периодических регистров), которые однозначно идентифицируют запись в регистре. Это "адрес" той записи, которую мы хотим изменить.Прочитать(). В этот момент менеджер обращается к базе данных и пытается найти запись по указанным ключам. Если находит, то загружает в свои свойства значения ее ресурсов и реквизитов.Выбран(), который вернет Истина, если чтение прошло успешно.Записать(). Этот метод сохранит все сделанные нами изменения в базу данных. Если запись с указанными ключами уже существовала, она будет обновлена. Если такой записи не было, будет создана новая.Посмотрим на практический пример. Допустим, нам нужно изменить цену для конкретного товара и типа цен на текущую дату в периодическом регистре сведений "Цены номенклатуры".
// Получаем ссылки на элементы справочников
СсылкаНаТовар = Справочники.Номенклатура.НайтиПоКоду("000123");
СсылкаНаТипЦен = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Оптовая");
НоваяЦена = 1500;
// 1. Создаем менеджер записи
МенеджерЗаписи = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
// 2. Заполняем ключевые поля для идентификации записи
МенеджерЗаписи.Номенклатура = СсылкаНаТовар;
МенеджерЗаписи.ТипЦен = СсылкаНаТипЦен;
МенеджерЗаписи.Период = ТекущаяДата();
// 3. Пытаемся прочитать запись из базы данных
МенеджерЗаписи.Прочитать();
// 4. Проверяем, была ли найдена запись
Если МенеджерЗаписи.Выбран() Тогда
// 5. Запись найдена, меняем значение ресурса "Цена"
МенеджерЗаписи.Цена = НоваяЦена;
// 6. Записываем изменения обратно в базу
МенеджерЗаписи.Записать();
Сообщить("Цена успешно обновлена!");
Иначе
Сообщить("Запись для указанного товара и типа цен не найдена.");
КонецЕсли;
Этот способ, как и было отмечено на форуме, является самым "безопасным", так как вы полностью контролируете процесс изменения одной конкретной записи.
Когда требуется изменить не одну, а сразу несколько записей, объединенных общим признаком (например, все цены для одного поставщика или все настройки для одного пользователя), на помощь приходит объект РегистрСведенийНаборЗаписей. Он позволяет работать с группой записей как с единым целым.
Важный момент: отбор в наборе записей работает только по точному равенству. Вы не можете установить отбор типа "больше", "меньше" или "В списке". Отбор устанавливается по значениям одного или нескольких измерений.
Рассмотрим алгоритм работы с набором записей:
СоздатьНаборЗаписей().Отбор набора и устанавливаем фильтр по нужным измерениям. Например, НаборЗаписей.Отбор.Поставщик.Установить(НужныйПоставщик).Прочитать() загружает в набор все записи из базы данных, которые удовлетворяют установленному отбору.НаборЗаписей.Добавить()) или удалять существующие (НаборЗаписей.Удалить()).Записать(). Он атомарно применяет все изменения к базе данных: обновляет измененные записи, добавляет новые и удаляет помеченные на удаление. Если установить свойство набора Замещать в Истина перед записью, то все старые записи, удовлетворяющие отбору, будут удалены, а вместо них записаны те, что находятся в наборе.Посмотрим на пример. Увеличим все цены конкретного поставщика на 10%.
// Получаем ссылку на нужного поставщика
СсылкаНаПоставщика = Справочники.Контрагенты.НайтиПоНаименованию("ООО Поставщик");
// 1. Создаем набор записей
НаборЗаписей = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
// 2. Устанавливаем отбор по измерению "Поставщик"
НаборЗаписей.Отбор.Поставщик.Установить(СсылкаНаПоставщика);
// 3. Считываем все записи, соответствующие отбору
НаборЗаписей.Прочитать();
// 4. Перебираем записи в наборе и изменяем их
Для Каждого Запись Из НаборЗаписей Цикл
// Увеличиваем цену на 10%
Запись.Цена = Запись.Цена * 1.10;
КонецЦикла;
// 5. Записываем весь измененный набор обратно в базу
НаборЗаписей.Записать();
Сообщить("Цены для поставщика " + СсылкаНаПоставщика + " успешно обновлены.");
Что делать, если условия отбора записей сложные и их невозможно задать через простой отбор набора записей? Например, нужно изменить записи, у которых значение одного ресурса больше определенного числа, или выбрать данные с соединением нескольких таблиц. В этом случае идеальным решением будет комбинация Запроса и Менеджера записи.
Этот подход сочетает лучшее от двух миров: мощь и гибкость языка запросов для выборки данных и безопасность менеджера записи для их поштучного изменения.
Именно этот способ был в итоге применен автором вопроса на форуме с помощью инструмента "Консоль запросов".
Алгоритм действий следующий:
Пока Выборка.Следующий() Цикл).Рассмотрим пример. Допустим, нам нужно обнулить остатки в регистре сведений "ОстаткиТоваровНаСкладах" для всех товаров из группы "Уцененные товары", у которых текущий остаток меньше 5 штук.
// 1. Составляем запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиТоваровНаСкладах.Склад,
| ОстаткиТоваровНаСкладах.Номенклатура
|ИЗ
| РегистрСведений.ОстаткиТоваровНаСкладах КАК ОстаткиТоваровНаСкладах
|ГДЕ
| ОстаткиТоваровНаСкладах.Номенклатура.Родитель = &ГруппаНоменклатуры
| И ОстаткиТоваровНаСкладах.Остаток < &МинимальныйОстаток";
// Находим нужную группу номенклатуры
ГруппаУценки = Справочники.Номенклатура.НайтиПоНаименованию("Уцененные товары");
Запрос.УстановитьПараметр("ГруппаНоменклатуры", ГруппаУценки);
Запрос.УстановитьПараметр("МинимальныйОстаток", 5);
// 2. Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// 3. Обходим результат и изменяем записи
Пока Выборка.Следующий() Цикл
// Для каждой строки выборки создаем свой менеджер
МенеджерЗаписи = РегистрыСведений.ОстаткиТоваровНаСкладах.СоздатьМенеджерЗаписи();
// Заполняем ключи из данных выборки
МенеджерЗаписи.Склад = Выборка.Склад;
МенеджерЗаписи.Номенклатура = Выборка.Номенклатура;
// Читаем запись. В данном случае это не обязательно,
// т.к. мы точно знаем, что она есть и просто хотим изменить ресурс.
// Но для надежности лучше всегда читать.
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
// Меняем ресурс
МенеджерЗаписи.Остаток = 0;
// Записываем
МенеджерЗаписи.Записать();
КонецЕсли;
КонецЦикла;
Сообщить("Обработка остатков завершена.");
Этот метод является наиболее универсальным и мощным, позволяя реализовывать практически любую логику массового изменения данных.
Записать() — это отдельная транзакция к базе данных. В таких случаях более производительным может оказаться использование Набора записей с методом Загрузить(), куда можно передать таблицу значений, предварительно подготовленную запросом.Выбирайте метод, который наиболее точно соответствует вашей задаче, и всегда тестируйте свои обработки на копии базы данных перед запуском на рабочей.