При работе с крупными базами данных перед разработчиками часто встает задача быстрой очистки или массового обновления данных. В классических реляционных базах данных для этого используются молниеносные операторы TRUNCATE TABLE и UPDATE. Однако в экосистеме 1С:Предприятие прямое обращение к таблицам СУБД таит в себе множество скрытых угроз — от юридических рисков до полного разрушения логической целостности базы данных. Разберем детально, почему прямое вмешательство в SQL-базу 1С является опасной практикой, к каким последствиям оно приводит и как решить эти задачи легально, быстро и безопасно.
Платформа 1С:Предприятие — это не просто визуальная оболочка над СУБД. Это сложная объектно-ориентированная среда, которая берет на себя управление транзакциями, кэшированием, объектными связями и бизнес-логикой. Прямое выполнение команд TRUNCATE TABLE или UPDATE на уровне СУБД приводит к критическим сбоям:
TRUNCATE сотрет эти строки. При следующем запуске системы или обновлении конфигурации платформа выдаст ошибку формата потока или аварийно завершит работу, так как не обнаружит обязательных системных записей.TRUNCATE, вы оставите миллионы "сиротских" (orphan) записей в подчиненных таблицах. Это гарантированно вызовет ошибки при первом же запуске утилиты проверки базы данных chdbfl.exe или штатного механизма "Тестирование и исправление".Посмотрим на практические способы решения этих проблем штатными средствами платформы 1С, которые работают безопасно и быстро.
Рассмотрим стандартный, но медленный код циклической перезаписи объектов:
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
мОбъект = Выборка.ПолучитьОбъект();
мОбъект.ОбменДанными.Загрузка = Истина;
мОбъект.Диспогруппа = СоответствиеДиспогрупп.Получить(Выборка.Производитель);
мОбъект.Записать();
КонецЦикла;
Выясним причину, почему данный код работает крайне медленно (например, обрабатывает всего 300 000 записей за целую ночь). Проблема заключается в том, что метод Записать() при каждом вызове инициирует отдельную транзакцию в СУБД. Это порождает колоссальные накладные расходы на открытие транзакции, запись лога транзакций (LDF) и коммит.
Для ускорения процесса сгруппируем запись объектов в пакеты с явным управлением транзакциями. Опытным путем установлено, что оптимальный размер пакета составляет от 1 000 до 5 000 объектов. Разберем по шагам правильную реализацию этого алгоритма:
Выборка = Справочники.Номенклатура.Выбрать();
РазмерПорции = 2000;
Счетчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
мОбъект = Выборка.ПолучитьОбъект();
мОбъект.ОбменДанными.Загрузка = Истина;
мОбъект.Диспогруппа = СоответствиеДиспогрупп.Получить(Выборка.Производитель);
Попытка
мОбъект.Записать();
Счетчик = Счетчик + 1;
Исключение
ОтменитьТранзакцию();
ВызватьИсключение "Ошибка при записи объекта: " + мОбъект.Ссылка + ". Описание: " + ОписаниеОшибки();
КонецПопытки;
Если Счетчик % РазмерПорции = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
Использование свойства ОбменДанными.Загрузка = Истина отключает выполнение тяжелых обработчиков событий ПередЗаписью и ПриЗаписи, а также большинство связанных подписок. А упаковка операций в транзакции по 2 000 элементов позволяет увеличить скорость обработки данных в 10–20 раз.
Чтобы задействовать все аппаратные ресурсы сервера и ускорить обработку сотен тысяч записей, разделим весь массив данных на независимые диапазоны и запустим их обработку параллельно в фоновых заданиях. Разберем пример реализации диспетчера параллельной обработки:
// Код в модуле обработки или менеджера, запускающий параллельные потоки
Процедура ЗапуститьПараллельноеОбновление() Экспорт
// Получаем список уникальных идентификаторов или кодов для разбиения на диапазоны
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа";
ТаблицаСсылок = Запрос.Выполнить().Выгрузить();
ВсегоЗаписей = ТаблицаСсылок.Количество();
КоличествоПотоков = 4; // Определяется мощностью сервера
РазмерПорции = Цел(ВсегоЗаписей / КоличествоПотоков);
Для НомерПотока = 1 По КоличествоПотоков Цикл
МассивПотока = Новый Массив;
НачальныйИндекс = (НомерПотока - 1) * РазмерПорции;
КонечныйИндекс = ?(НомерПотока = КоличествоПотоков, ВсегоЗаписей - 1, НачальныйИндекс + РазмерПорции - 1);
Для Индекс = НачальныйИндекс По КонечныйИндекс Цикл
МассивПотока.Добавить(ТаблицаСсылок[Индекс].Ссылка);
КонецЦикла;
ПараметрыПроцедуры = Новый Массив;
ПараметрыПроцедуры.Добавить(МассивПотока);
// Запускаем фоновое задание
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьОбновлениеПорции", ПараметрыПроцедуры);
КонецЦикла;
КонецПроцедуры
Метод ВыполнитьОбновлениеПорции в общем модуле должен принимать массив ссылок, открывать транзакцию и обрабатывать переданную порцию данных по схеме, описанной в Решении 1. Это сократит время выполнения масштабных операций с нескольких часов до считанных минут.
Проанализируем ситуацию: если справочник регулярно полностью очищается и перезаполняется внешними данными (например, это справочник загружаемых кодов, временных классификаторов или КЛАДР), то использование справочника как прикладного объекта неоптимально. Справочники имеют избыточные системные поля (GUID, код, наименование, иерархия, предопределенные элементы), которые сильно нагружают базу данных.
Вместо этого гораздо эффективнее использовать независимый регистр сведений. Платформа 1С позволяет очищать независимые регистры сведений моментально штатными средствами без построчного удаления записей. Рассмотрим, как это сделать:
НаборЗаписей = РегистрыСведений.МойСлужебныйРегистр.СоздатьНаборЗаписей();
// Не устанавливая отборы, записываем пустой набор данных
НаборЗаписей.Записать(Истина);
Этот метод работает на уровне СУБД аналогично операторам быстрой очистки таблиц, полностью удаляя все записи регистра в рамках одной быстрой транзакции. Метод абсолютно безопасен для платформы, не нарушает ссылочную целостность, не оставляет "сиротских" записей в структуре СУБД и полностью разрешен лицензионным соглашением 1С.
Иногда после изменения сотен тысяч элементов реквизитов база данных лавинообразно увеличивается в объеме (например, с 80 до 100 Гб), а системный администратор сообщает о переполнении диска. Разберем причины этого явления:
Full файл лога будет раздуваться до тех пор, пока не будет сделан бэкап транзакций. Перед проведением глобальных обновлений в рабочей базе рекомендуется временно переводить базу в модель восстановления Simple (простым языком — модель без журналирования транзакций), а после завершения операции возвращать ее в Full и делать полный бэкап.Соблюдение этих простых архитектурных и программных правил гарантирует высокую скорость обработки данных, сохраняя стабильность, безопасность и юридическую чистоту вашей информационной системы 1С.