Работа с большими объемами данных в 1С:Предприятие всегда требует особого подхода. Когда в независимом непериодическом регистре сведений накапливается 50 миллионов записей, стандартные методы «удаления в лоб» перестают работать — система может зависнуть на часы или вызвать переполнение лога транзакций SQL-сервера. В данной статье мы подробно разберем, как решить задачу фильтрации такого регистра, чтобы оставить в нем только уникальные связки Код — Производитель с минимальной ценой.
Проанализируем входные данные: у нас есть независимый регистр сведений с измерениями Код, Производитель и ресурсом Цена. Наша цель — избавиться от избыточности, оставив для каждой пары ключ-производитель только одну запись с наименьшей стоимостью. При объеме в 50 млн записей важно понимать, какой процент данных подлежит удалению. Если это 80% и более (как в нашем случае), то гораздо быстрее не удалять лишнее, а пересоздать данные «с нуля».
Это классический способ, подходящий, если сервер приложений обладает достаточным объемом оперативной памяти. Разберем его по шагам:
Рассмотрим пример кода для реализации этого метода:
// 1. Получаем нужные данные запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Код КАК Код,
| Цены.Производитель КАК Производитель,
| МИНИМУМ(Цены.Цена) КАК Цена
|ИЗ
| РегистрСведений.ЦеныПоставщиков КАК Цены
|ГРУППИРОВАТЬ ПО
| Цены.Код,
| Цены.Производитель";
РезультатЗапроса = Запрос.Выполнить();
ТаблицаЧистыхДанных = РезультатЗапроса.Выгрузить();
// 2. Очищаем регистр полностью (быстрая операция для независимого регистра)
НаборДляОчистки = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
НаборДляОчистки.Записать(Истина);
// 3. Записываем данные порциями для стабильности
// Вместо построчного Добавить() используем Метод Загрузить()
НаборЗаписей = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
НаборЗаписей.Загрузить(ТаблицаЧистыхДанных);
НаборЗаписей.Записать();
Важный нюанс: метод Загрузить() работает значительно быстрее, чем Добавить() в цикле, так как платформа оптимизирует передачу таблицы значений во внутренние структуры набора. Однако при 10–15 млн оставшихся записей попытка записать их одним набором может привести к ошибке памяти. В этом случае мы рекомендуем разбивать таблицу ТаблицаЧистыхДанных на порции по 100 000 – 500 000 строк.
Проанализируем ситуацию, когда нельзя прерывать работу пользователей на длительное время. В этом случае удобно использовать «клон» регистра.
ЦеныПоставщиков_Буфер с идентичной структурой.Этот метод хорош тем, что основная таблица остается доступной для чтения во время подготовки данных в буфере. Выясним причину популярности этого метода у системных администраторов: он минимизирует время блокировки основной таблицы базы данных.
Для программистов, имеющих доступ к MS SQL Server или PostgreSQL, существует «реактивный» способ. Рассмотрим его подробнее. Операции на уровне SQL позволяют избежать накладных расходов сервера приложений 1С.
Алгоритм действий:
ПолучитьСтруктуруХраненияБазыДанных().SELECT ... MIN(Цена) ... GROUP BY.TRUNCATE TABLE для мгновенной очистки исходного регистра. Это работает на порядки быстрее DELETE, так как не пишет каждую строку в лог транзакций.INSERT INTO ... SELECT.Внимание: при работе с SQL напрямую индексы могут замедлять вставку. Проанализируем совет из практики: перед массовой заливкой 20 млн строк стоит отключить (Drop) индексы в SQL, а после завершения — пересоздать их. Это сэкономит до 50% времени.
Если мы не хотим полностью очищать регистр, а планируем именно удалять лишнее (например, если удалению подлежит лишь малая часть данных), воспользуемся пакетным методом. Рассмотрим пошагово:
При использовании НаборЗаписей крайне важно не устанавливать отборы слишком часто. Если мы удаляем записи по условию, лучше всего делать это транзакциями. Посмотрим на пример оптимизированного цикла:
// Пример пакетной записи (сообщение 18 обсуждения)
РазмерПорции = 10000;
Счетчик = 0;
НачатьТранзакцию();
Для Каждого СтрокаДанных Из МассивДанных Цикл
// Логика подготовки записи...
Счетчик = Счетчик + 1;
Если Счетчик % РазмерПорции = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Однако, как отметили участники обсуждения, для 50 млн записей построчный обход — это худший вариант. Всегда стремитесь к использованию НаборЗаписей.Загрузить().
Выясним, на что еще нужно обратить внимание при работе с такими объемами:
Итог совместной работы: Мы разобрали несколько сценариев. Самый эффективный для 50 млн записей — это выборка уникальных данных во временную таблицу (или ТЗ), очистка регистра методом Записать(Истина) и последующая порционная загрузка через Набор.Загрузить(). Это гарантирует высокую скорость и стабильность системы.