При работе с большими массивами данных в 1С программисты часто сталкиваются с проблемой производительности при модификации табличных частей (ТЧ). Стандартный метод Вставить(), отлично работающий на 10-20 строках, превращается в «узкое горлышко», когда речь заходит о добавлении 5 000 строк в таблицу, где уже содержится 10 000 записей. В этой статье мы разберем, почему это происходит, и проанализируем наиболее эффективные способы ускорения этой операции.
Прежде чем перейти к решениям, выясним причину деградации производительности. Когда мы используем метод Вставить() для каждой новой строки, платформа 1С вынуждена после каждой итерации физически сдвигать в памяти все последующие строки. Если мы вставляем строку в начало таблицы из 10 000 элементов, система выполняет 10 000 операций копирования. При вставке 5 000 строк общее количество элементарных операций становится колоссальным. Кроме того, если действие происходит в контексте формы, каждое изменение может вызывать пересчет итогов, проверку связей параметров выбора и перерисовку графического интерфейса.
Рассмотрим один из самых изящных способов — использование дополнительного поля для сортировки в ТаблицеЗначений. Вместо того чтобы «раздвигать» строки, мы добавим новые данные в конец и просто пересортируем таблицу.
Алгоритм действий:
ТаблицаЗначений.ИндексСортировки (тип Число).НомерСтроки * 10. Это создаст «зазоры» между строками.ИндексСортировки так, чтобы они попали в нужный диапазон (например, между 1000-й и 1001-й строкой оригинала).Сортировать() по колонке ИндексСортировки.Загрузить().Проанализируем пример кода для этого метода:
// Выгружаем данные из ТЧ во временную таблицу
ТЗ = Объект.Товары.Выгрузить();
ТЗ.Колонки.Добавить("ИндексСортировки", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 0)));
// Заполняем веса для существующих строк
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтрокаТЗ.ИндексСортировки = ТЗ.Индекс(СтрокаТЗ) * 100;
КонецЦикла;
// Добавляем новые строки (например, после 100-й строки оригинала)
ПозицияВставки = 100 * 100;
Для Счетчик = 1 По 5000 Цикл
НоваяСтр = ТЗ.Добавить();
НоваяСтр.Номенклатура = СсылкаНаНоменклатуру;
// Смещение внутри блока вставки
НоваяСтр.ИндексСортировки = ПозицияВставки + Счетчик;
КонецЦикла;
// Сортируем и возвращаем в ТЧ
ТЗ.Сортировать("ИндексСортировки Возр");
Объект.Товары.Загрузить(ТЗ);
Если нам точно известно место вставки, мы можем разделить исходную таблицу на две части, добавить новые строки между ними и собрать все воедино. Этот способ исключает медленную сортировку и работает максимально быстро.
Разберем ситуацию по шагам:
ТаблицаЗначений с идентичной структурой.Загрузить().Посмотрим на пример реализации:
// Индекс, после которого нужно вставить данные
ИндексВставки = 1000;
ТЗ_Оригинал = Объект.Товары.Выгрузить();
ТЗ_Результат = ТЗ_Оригинал.СкопироватьКолонки();
// 1. Копируем первую часть
Для Ит = 0 По ИндексВставки - 1 Цикл
ЗаполнитьЗначенияСвойств(ТЗ_Результат.Добавить(), ТЗ_Оригинал[Ит]);
КонецЦикла;
// 2. Добавляем новые данные (допустим, они в ТЗ_Новые)
Для Каждого СтрНовая Из ТЗ_Новые Цикл
ЗаполнитьЗначенияСвойств(ТЗ_Результат.Добавить(), СтрНовая);
КонецЦикла;
// 3. Копируем остаток
Для Ит = ИндексВставки По ТЗ_Оригинал.Количество() - 1 Цикл
ЗаполнитьЗначенияСвойств(ТЗ_Результат.Добавить(), ТЗ_Оригинал[Ит]);
КонецЦикла;
Объект.Товары.Загрузить(ТЗ_Результат);
Важное замечание: Метод Загрузить() работает значительно быстрее, чем Добавить() в цикле непосредственно в табличную часть, так как он минимизирует количество вызовов механизмов платформы по обновлению данных.
Если данные для вставки уже находятся в базе данных или могут быть помещены во временную таблицу, использование СУБД для компоновки результата — самый производительный вариант. Проанализируем ситуацию, когда нам нужно объединить данные на сервере. При работе с этим механизмом также полезно учитывать особенности и проблемы добавления данных во временные таблицы, появившиеся в платформе 8.3.25.
Выясним причину эффективности этого метода: СУБД оптимизированы для работы с наборами данных. Операция ОБЪЕДИНИТЬ ВСЕ с последующей выгрузкой в ТЗ займет доли секунды даже для десятков тысяч строк.
Рассмотрим структуру такого запроса:
ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Старые ИЗ &ТЗ_Старые КАК ТЗ;
ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Новые ИЗ &ТЗ_Новые КАК ТЗ;
ВЫБРАТЬ *, НомерСтроки КАК Порядок ИЗ ВТ_Старые ГДЕ НомерСтроки <= &Порог
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ *, &Порог + 0.1 ИЗ ВТ_Новые
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ *, НомерСтроки КАК Порядок ИЗ ВТ_Старые ГДЕ НомерСтроки > &Порог
УПОРЯДОЧИТЬ ПО Порядок
Помимо выбора алгоритма вставки, следует обратить внимание на инфраструктурные моменты:
1. Контекст исполнения. Все манипуляции с 5 000 строками должны происходить строго на стороне сервера. Передача такого объема данных с клиента на сервер в цикле (по одной строке) — верный способ «повесить» систему. Используйте схему: ДанныеФормыВЗначение -> Работа с объектом в серверной процедуре -> ЗначениеВДанныеФормы.
2. Отключение обработчиков событий. Если у табличной части есть обработчик ПриИзменении или ПередДобавлением, он будет вызываться для каждой строки (если вы не используете Загрузить). Перед массовой вставкой рекомендуется устанавливать специальный флаг (переменную модуля), который позволит обработчикам «понимать», что сейчас идет программное заполнение, и не выполнять лишние расчеты.
3. Отложенные расчеты и визуализация. Если в таблице много вычисляемых колонок (суммы, налоги), не считайте их в момент добавления каждой строки. Вставьте «сырые» данные, а затем запустите одну общую процедуру пересчета для всей коллекции. Также полезно использовать метод ЭтаФорма.ОбновитьОтображениеДанных() только один раз в самом конце операции.
4. Индексы в Таблице Значений. Помните, что индексы в ТаблицеЗначений (метод Индексы.Добавить) ускоряют поиск, но замедляют вставку и удаление, так как системе нужно перестраивать дерево индексов. Если вы активно модифицируете таблицу, сначала удалите индексы, выполните вставку, а затем создайте их заново.
Подводя итог, можно сказать, что для быстрой вставки 5 000 строк в середину ТЧ лучшей стратегией будет использование метода Загрузить() в сочетании с предварительной подготовкой полной ТаблицыЗначений на сервере. Это позволит сократить время операции с нескольких минут до долей секунды.