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