В процессе разработки на платформе 1С:Предприятие 7.7 часто возникает задача разделения одной таблицы значений (ТЗ) на две или более части по определенному условию. Например, когда необходимо разбить строки документа на те, что соответствуют определенному критерию (наличие остатка, тип товара, флаг обработки), и все остальные. При работе с большими объемами данных (10 000 строк и более) критически важным становится вопрос производительности, так как стандартные методы перебора и удаления строк могут работать медленно.
В этой статье мы подробно разберем несколько подходов: от классического перебора до оптимизированного метода с использованием сортировки, который показывает лучшие результаты по скорости.
Этот метод считается наиболее эффективным в среде 7.7, так как он минимизирует количество тяжелых операций удаления отдельных строк. Мы не удаляем строки по одной, а группируем их и выгружаем целыми блоками. Рассмотрим логику процесса:
Ключ).Итог(), чтобы узнать количество строк, подходящих под условие.Выгрузить() копируем нужные диапазоны строк в новые таблицы.Проанализируем пример кода для этого метода:
// ТЗ_Вход - исходная таблица
КоличествоНужных = 0;
ТЗ_Вход.НоваяКолонка("Ключ", "Число", 1, 0);
// Заполняем признак
ТЗ_Вход.ВыбратьСтроки();
Пока ТЗ_Вход.ПолучитьСтроку() = 1 Цикл
Если ТЗ_Вход.Количество > 0 Тогда // Ваше условие
ТЗ_Вход.Ключ = 1;
Иначе
ТЗ_Вход.Ключ = 0;
КонецЕсли;
КонецЦикла;
// Суммируем количество строк с признаком 1
КоличествоНужных = ТЗ_Вход.Итог("Ключ");
// Группируем подходящие строки в начале таблицы
ТЗ_Вход.Сортировать("-Ключ");
// Создаем две новые таблицы
ТЗ1 = СоздатьОбъект("ТаблицаЗначений");
ТЗ2 = СоздатьОбъект("ТаблицаЗначений");
// Выгружаем блоки строк
ТЗ_Вход.Выгрузить(ТЗ1, 1, КоличествоНужных);
ТЗ_Вход.Выгрузить(ТЗ2, КоличествоНужных + 1, ТЗ_Вход.КоличествоСтрок());
// Удаляем временную колонку, если она больше не нужна
ТЗ1.УдалитьКолонку("Ключ");
ТЗ2.УдалитьКолонку("Ключ");
Преимущество: Скорость работы. Для таблицы в 10 000 строк этот метод работает примерно в 2 раза быстрее, чем построчное копирование, и в 8-10 раз быстрее, чем построчное удаление.
Если по каким-то причинам вы не хотите создавать новые таблицы, а планируете модифицировать текущую (оставив в ней только часть данных), следует использовать метод удаления. Важный нюанс 1С 7.7: при удалении строки методом УдалитьСтроку() индексы всех последующих строк сдвигаются. Если обходить таблицу с начала, вы рискуете пропустить строки или вызвать ошибку обращения к индексу.
Разберем, как правильно организовать обратный цикл:
// ТЗ - исходная таблица, ТЗ2 - таблица для "отсеянных" строк
ТЗ2 = СоздатьОбъект("ТаблицаЗначений");
ТЗ.Выгрузить(ТЗ2, 0, 0); // Копируем структуру колонок
КолСтрок = ТЗ.КоличествоСтрок();
Для Ном = -КолСтрок По -1 Цикл
ТекущийНомер = -Ном;
ТЗ.ПолучитьСтрокуПоНомер(ТекущийНомер);
Если УсловиеВыполнено = 0 Тогда
// Копируем данные в ТЗ2 перед удалением из ТЗ
ТЗ2.НоваяСтрока();
// Здесь придется копировать значения колонок вручную
// или использовать метод Заполнить(ТЗ,...)
ТЗ.УдалитьСтроку(ТекущийНомер);
КонецЕсли;
КонецЦикла;
Важно: Помните, что метод УдалитьСтроку() для больших таблиц работает крайне медленно, так как после каждого удаления 1С пересчитывает внутренние связи объектов в памяти.
Рассмотрим ситуацию, когда нам нужно быстро перебросить данные из одной строки в другую без перебора всех имен колонок. В 1С 7.7 для этого удобно использовать метод Заполнить(). Он позволяет скопировать содержимое одной таблицы в другую (или в ее часть), если структуры колонок совпадают.
Процедура РазделитьТЗ(ТЗ_вход, ТЗ1, ТЗ2)
ТЗ1 = СоздатьОбъект("ТаблицаЗначений");
ТЗ2 = СоздатьОбъект("ТаблицаЗначений");
ТЗ_врем = СоздатьОбъект("ТаблицаЗначений");
// Создаем структуру в новых ТЗ (выгружаем 0 строк)
ТЗ_вход.Выгрузить(ТЗ1, 0, 0);
ТЗ_вход.Выгрузить(ТЗ2, 0, 0);
ТЗ_вход.ВыбратьСтроки();
Пока ТЗ_вход.ПолучитьСтроку() = 1 Цикл
// Копируем одну строку во временную ТЗ для использования метода Заполнить
ТЗ_вход.Выгрузить(ТЗ_врем, ТЗ_вход.НомерСтроки, ТЗ_вход.НомерСтроки);
Если ТЗ_вход.Количество > 0 Тогда
ТЗ1.НоваяСтрока();
ТЗ1.Заполнить(ТЗ_врем, ТЗ1.КоличествоСтрок(), ТЗ1.КоличествоСтрок());
Иначе
ТЗ2.НоваяСтрока();
ТЗ2.Заполнить(ТЗ_врем, ТЗ2.КоличествоСтрок(), ТЗ2.КоличествоСтрок());
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Этот метод избавляет от необходимости писать ТЗ1.Товар = ТЗ_вход.Товар для каждой колонки, что делает код универсальным для любой структуры данных.
Проанализируем несколько приемов, которые помогут ускорить работу с таблицами еще сильнее:
Выгрузить(ТЗ_Новая, 1, 1) и последующего УдалитьСтроки(), используйте ТЗ_Исходная.Выгрузить(ТЗ_Новая, 0, 0). Это создаст пустую таблицу с идентичным набором и типами колонок.Индексированной Таблицы Значений из библиотеки 1CPP. Она позволяет накладывать фильтры (УстановитьФильтр), которые работают практически мгновенно.1SQLite) и выполнить разделение обычным SELECT запросом.Выясним причину выбора конкретного метода: если таблица содержит до 1000 строк, разница в производительности между способами будет незаметна. Однако для тяжелых отчетов или обработок загрузки данных всегда отдавайте предпочтение Способу №1 (через Сортировать и Выгрузить), так как он наиболее эффективно использует внутренние механизмы платформы 7.7.