Программное изменение данных: объекты, наборы записей и запросы
Содержание
Данные в 1С 8.3 меняют двумя путями: справочники и документы — через объект (ПолучитьОбъект() → правка реквизитов → Записать()), а регистры — через набор записей. Напрямую из языка запросов изменить ничего нельзя: в языке запросов 1С нет оператора UPDATE, запрос только выбирает данные, а пишет их уже код.
Этот разбор — для разработчика и администратора, которому нужно поправить реквизиты пачкой, пересчитать регистр или прогнать разовую трансформацию через внешнюю обработку . Все примеры — для платформы 8.3 (интерфейс «Такси»), встроенный язык, толстый и тонкий клиент.
Любое массовое изменение делайте на копии базы. Запись объектом запускает проверки и подписки на события — результат предсказать сложнее, чем кажется. Перед прогоном в рабочей базе снимите резервную копию.
Объектный способ: читаем, меняем реквизит, записываем
Базовый приём для справочников и документов — получить объект по ссылке, изменить реквизит, записать. Ссылку находят по коду, наименованию, реквизиту или через запрос.
// Поменять единицу измерения у одного элемента номенклатуры
Ссылка = Справочники.Номенклатура.НайтиПоКоду("000000123");
Если Не Ссылка.Пустая() Тогда
Объект = Ссылка.ПолучитьОбъект();
Объект.ЕдиницаИзмерения = Справочники.ЕдиницыИзмерения.НайтиПоНаименованию("шт");
Объект.Записать();
КонецЕсли;
Ключевые моменты объектного способа:
- 🔹
НайтиПоКодуиНайтиПоНаименованиювозвращают ссылку; если ничего не найдено — пустую ссылку, её проверяют методомПустая(). - 🔹
ПолучитьОбъект()читает объект целиком и блокирует его на запись. - 🔹 Для документа запись с проведением —
Объект.Записать(РежимЗаписиДокумента.Проведение), без проведения —Объект.Записать(РежимЗаписиДокумента.Запись). - 🔹 Объектная запись выполняет проверку заполнения, обработчики
ПередЗаписью,ПриЗаписии подписки на события. Это и плюс (данные останутся корректными), и минус (медленно на больших объёмах).
Иногда нужно записать «как есть», не запуская бизнес-логику, — например, при переносе данных. Для этого ставят флаг загрузки:
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();
ОбменДанными.Загрузка = Истина отключает автонумерацию, проверку заполнения и часть обработчиков. Пользуйтесь им осознанно: так легко записать объект, который не прошёл бы обычную проверку.
Как изменить реквизит у группы элементов пачкой
Массового «одной командой» в 1С нет — меняют циклом по результату запроса. Запрос отбирает нужные ссылки, цикл получает объект и записывает.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Ссылка
|ИЗ Справочник.Номенклатура
|ГДЕ НЕ ПометкаУдаления
| И Поставщик = &Поставщик";
Запрос.УстановитьПараметр("Поставщик", ВыбранныйПоставщик);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Объект.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
Объект.Записать();
КонецЦикла;
Почему именно так, а не «обновлением в запросе»:
| Инструмент | Что умеет | Изменение данных |
|---|---|---|
| Язык запросов | Выборка, соединения, группировка, итоги | Нет — только чтение |
Объект (ПолучитьОбъект) | Полная бизнес-логика, проверки | Да, по одному |
| Набор записей | Прямая работа с таблицей регистра | Да, пачкой |
Запрос в этой схеме делает одну работу — быстро отбирает ссылки по условию. Саму запись выполняет объект. Выбирайте только поле Ссылка, а не все реквизиты: лишние данные замедляют выборку, а объект всё равно перечитывается целиком.
Регистры сведений и накопления: работа через набор записей
Регистры объектом не меняют — у них нет «объекта» в том же смысле. Для регистра создают набор записей, накладывают отбор, читают, правят в памяти и записывают.
Регистр сведений — самый частый случай ручной правки (курсы валют, цены, настройки):
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(ТоварСсылка);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
Запись.Цена = Запись.Цена * 1.1;
КонецЦикла;
Набор.Записать();
Как ведёт себя набор записей:
- 🔸
Записать()без параметров замещает все записи по отбору — то есть сначала удаляет старые по этому отбору, затем пишет новые. Если набор прочитан не полностью, часть данных пропадёт. - 🔸
Набор.Записать(Ложь)— режим добавления: старые записи по отбору не трогаются, новые дописываются. - 🔸 Отбор нужно задавать по тем измерениям, по которым регистр позволяет частичную запись (у независимого регистра сведений — по ведущему измерению или полному набору).
Регистры накопления (остатки, обороты) в норме двигают документы. Прямая запись в них — приём для исправления «съехавших» остатков или разовой корректировки. Техника та же — набор записей с отбором по регистратору, но ответственность выше: ручные движения легко рассинхронизировать с документами.
Если пишете движения регистра напрямую, не через проведение документа, отбор обычно ставят по измерению Регистратор. Движения, сделанные в обход документа, при перепроведении документа исчезнут.
Транзакции: почему массовую правку оборачивают в них
Транзакция делает группу изменений атомарной: либо записались все, либо ни одного. Без неё сбой на 500-м объекте из 1000 оставит базу в полуобработанном состоянии.
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Объект.Комментарий = "Обработано";
Объект.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение; // пробрасываем ошибку дальше, не глотаем
КонецПопытки;
Правила, которые экономят много времени при разборе инцидентов:
- 🔹 На каждый
НачатьТранзакцию()обязан быть ровно одинЗафиксироватьТранзакцию()илиОтменитьТранзакцию(). Несбалансированные транзакции — частая причина «зависших» блокировок. - 🔹 Не «проглатывайте» исключение в блоке
ИсключениебезВызватьИсключение— иначе код продолжит работу так, будто всё хорошо, а данные откатились. - 🔹 Одна гигантская транзакция на миллион записей держит блокировки до самого конца и раздувает журнал. Для массовых операций транзакцию дробят на порции (см. раздел про оптимизацию).
Для защиты от параллельного изменения тех же данных используют управляемые блокировки внутри транзакции — объект БлокировкаДанных. Он резервирует строки до фиксации, чтобы два сеанса не переписали одно и то же.
НачатьТранзакцию();
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.ЦеныНоменклатуры");
Элемент.Режим = РежимБлокировкиДанных.Исключительный;
Элемент.УстановитьЗначение("Номенклатура", ТоварСсылка);
Блокировка.Заблокировать();
// чтение и запись набора — уже под блокировкой
ЗафиксироватьТранзакцию();
Исключительный режим не даст другому сеансу ни прочитать, ни изменить заблокированные записи до фиксации; разделяемый — только запретит их менять. Блокировку ставят сразу после НачатьТранзакцию() и до чтения данных, иначе между чтением и записью успеет вклиниться чужой сеанс.
Сложные операции: наборы записей, менеджеры и прямой доступ
Когда правка выходит за «поменять один реквизит», в ход идут более гибкие механизмы.
Менеджер записи регистра сведений. Если регистр подчинён регистратору или нужно изменить/добавить одну конкретную запись без чтения всего набора, удобнее менеджер:
Запись = РегистрыСведений.КурсыВалют.СоздатьМенеджерЗаписи();
Запись.Период = ТекущаяДата();
Запись.Валюта = ВалютаСсылка;
Запись.Курс = 95.5;
Запись.Кратность = 1;
Запись.Записать(Истина); // Истина — замещать, если запись с таким ключом уже есть
Правка табличных частей. Строки документа или справочника — это коллекция Объект.Товары. Их добавляют (Добавить()), удаляют (Удалить(Строка)) и меняют в цикле. При удалении строк в цикле идите с конца коллекции, иначе индексы «съедут»:
Для Сч = Объект.Товары.Количество() - 1 По 0 Цикл
Если Объект.Товары[Сч].Количество = 0 Тогда
Объект.Товары.Удалить(Сч);
КонецЕсли;
КонецЦикла;
Групповая обработка справочников и документов — типовой механизм для правки без кода. Открывается через Все функции → Стандартные → Групповое изменение реквизитов (в типовых конфигурациях на БСП — НСИ и администрирование → Обслуживание → Корректировка данных → Групповое изменение реквизитов). Подходит, когда менять нужно один-два реквизита по отбору и писать обработку ради этого смысла нет.
Внешние обработки для разовых изменений
Разовую правку логично оформить не кодом в модуле, а внешней обработкой (.epf) — её не нужно встраивать в конфигурацию и легко удалить после.
Порядок работы:
- В конфигураторе создайте внешнюю обработку, добавьте модуль с процедурой или форму с кнопкой.
- Поместите код изменения в обработчик команды или в процедуру с директивой
&НаСервере. - Сохраните
.epf, в режиме «1С:Предприятие» откройте черезФайл → Открытьи запустите.
&НаСервере
Процедура ВыполнитьНаСервере()
// отбор и цикл правки — здесь, на сервере
КонецПроцедуры
Код, который массово читает и пишет данные, держите в контексте &НаСервере или в общем серверном модуле. Клиентский код не имеет прямого доступа к объектам базы и будет гонять данные между клиентом и сервером на каждой итерации.
Для сложных сценариев, где нужно менять структуру (добавлять реквизиты, а не только значения), используют расширения конфигурации — они появились в платформе 8.3.6 и позволяют дорабатывать типовую без снятия с поддержки.
Типичные ошибки при программном изменении
Ошибки, которые повторяются чаще всего и которые не очевидны из синтаксиса:
- 🔴 Ждут от запроса изменения данных. Пишут
UPDATEилиВЫБРАТЬ ... ИЗМЕНИТЬ— таких конструкций в языке запросов 1С нет. Запрос только читает. - 🔴 Замещают набор записей неполным чтением. Прочитали набор с узким отбором, а
Записать()затёр записи по более широкому ключу. Проверяйте, по каким измерениям разрешена частичная запись. - 🔴 Меняют табличную часть в прямом цикле. Удаление строк по возрастающему индексу пропускает элементы. Идите с конца.
- 🔴 Не ловят исключение в транзакции. Сбой на середине оставляет открытую транзакцию и заблокированные данные до конца сеанса.
- 🔴 Пишут объект в клиентском контексте в цикле. Каждая итерация — обращение к серверу; на тысячах объектов это минуты вместо секунд.
- 🔴 Забывают про проведение документа.
Объект.Записать()без режима проведения не сформирует движения по регистрам — остатки не изменятся.
Проверка заполнения, которую запускает обычная запись, — не помеха, а страховка. Отключать её флагом загрузки стоит только когда вы точно знаете, что пишете корректные данные.
Оптимизация массовых изменений
На десятках тысяч объектов разница между наивным и аккуратным кодом — это минуты против часов и блокировки для всех пользователей.
Что реально ускоряет и разгружает базу:
- Дробите транзакцию на порции. Не одна транзакция на весь объём, а фиксация каждые 500–1000 объектов. Так блокировки держатся недолго и журнал не раздувается.
- Выбирайте в запросе только
Ссылка. Объект всё равно перечитывается приПолучитьОбъект(), поэтому тащить все реквизиты в выборку бессмысленно. - Отключайте лишние проверки осознанно. Если данные заведомо корректны и бизнес-логика не нужна, флаг
ОбменДанными.Загрузкаубирает проверку заполнения и часть подписок. - Тяжёлые прогоны — в фоновом задании. Длительную обработку запускают через
ДлительныеОперацииили фоновое задание, а не в основном сеансе, который «повиснет» на время работы. - Отбирайте на стороне СУБД. Пусть запрос с условием
ГДЕвернёт 200 нужных ссылок, а не 200 000 с отбором в циклеЕсли.
Счётчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Объект.ОбменДанными.Загрузка = Истина;
Объект.Статус = НовыйСтатус;
Объект.Записать();
Счётчик = Счётчик + 1;
Если Счётчик % 1000 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Для разовой правки на боевой базе выбирайте время минимальной нагрузки. Даже аккуратные порционные блокировки на активной базе могут столкнуться с работой пользователей.
Частые вопросы
Можно ли изменить данные прямо из языка запросов 1С?
UPDATE в нём нет. Запрос отбирает данные, а изменяет их код: объектом (справочники, документы) или набором записей (регистры).Чем отличается запись объектом от записи набором записей?
ПолучитьОбъект → Записать) запускает проверки заполнения, обработчики и подписки на события — подходит для справочников и документов. Набор записей пишет напрямую в таблицу регистра без этой логики — это единственный способ менять регистры.Как изменить данные, не запуская обработчики и проверки?
Объект.ОбменДанными.Загрузка = Истина перед Записать(). Это отключает автонумерацию, проверку заполнения и часть подписок. Применять только к заведомо корректным данным.Почему после массовой правки документов не изменились остатки?
Объект.Записать(РежимЗаписиДокумента.Проведение).