В процессе разработки на платформе 1С:Предприятие 8 часто возникает задача группировки данных. Основным инструментом для этого в оперативной памяти является метод Свернуть() объекта ТаблицаЗначений. Однако начинающие разработчики часто сталкиваются с ситуацией, когда метод вызывается, но строки с одинаковыми ключевыми полями остаются на месте. Рассмотрим подробно, почему это происходит, и проанализируем правильные подходы к решению этой задачи.
Проанализируем фрагмент кода, который часто становится причиной недоумения. Основная ошибка заключается не в самом методе, а в алгоритмическом порядке действий. Рассмотрим ситуацию, когда разработчик сначала инициализирует таблицу, сразу вызывает метод Свернуть(), а затем начинает наполнять её данными в цикле.
В этом случае метод Свернуть() отрабатывает на абсолютно пустой таблице. Поскольку в ней еще нет строк, группировать нечего. Последующее добавление строк через метод Добавить() просто наполняет таблицу дублями, которые уже никто не сворачивает. Выясним, как должен выглядеть правильный алгоритм:
ТаблицаЗначений и определение его колонок (в этом поможет генератор, создающий код новой таблицы значений).ТабличныйДокумент или файл Excel) и заполнение таблицы всеми строками, включая дубли.Посмотрим на пример правильной последовательности действий:
// 1. Создаем и типизируем ТЗ
ДанныеФайла = Новый ТаблицаЗначений;
ДанныеФайла.Колонки.Добавить("НомерЗаказа", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
ДанныеФайла.Колонки.Добавить("СуммаДоставки", Новый ОписаниеТипов("Число", , , Новый КвалификаторыЧисла(15,2)));
// 2. Заполняем ТЗ всеми данными из источника
Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
НоваяСтрока = ДанныеФайла.Добавить();
НоваяСтрока.НомерЗаказа = СокрЛП(ТабДок.Область(НомерСтроки, 1).Текст);
// Обязательно приводим к числу, чтобы метод Свернуть корректно суммировал
НоваяСтрока.СуммаДоставки = Число(ТабДок.Область(НомерСтроки, 2).Текст);
КонецЦикла;
// 3. Вот теперь сворачиваем!
// Первый параметр - колонки группировки, второй - колонки суммирования
ДанныеФайла.Свернуть("НомерЗаказа", "СуммаДоставки");
// 4. Обрабатываем результат
Для Каждого СтрокаТЗ Из ДанныеФайла Цикл
// Здесь выполняем действия с уникальными заказами
КонецЦикла;
Даже если мы вызываем метод в правильном месте, строки могут не свернуться. Разберем вторую по частоте причину — неявное различие в значениях. Для метода Свернуть() значения " 123" и "123" — это разные ключи группировки.
Рассмотрим подробнее факторы, влияющие на сравнение:
СокрЛП() при заполнении таблицы.ОписаниеТипов. Это гарантирует, что при присваивании значения оно будет автоматически приведено к нужному типу.СокрЛП() его не удаляет. В таких случаях нужно использовать СтрЗаменить(Текст, Символы.НПП, "").Проанализируем ситуацию с суммированием. Если вы хотите, чтобы числовые данные складывались, убедитесь, что колонка, указанная во втором параметре метода Свернуть(), имеет тип Число. Если там будет тип Строка, платформа не выдаст ошибку, но и суммировать значения не будет, просто оставив первое попавшееся значение для группы.
Часто в процессе группировки разработчики пытаются выполнять бизнес-логику (например, поиск документа по номеру и его проведение) прямо внутри цикла заполнения таблицы. Это приводит к двум негативным последствиям:
НайтиПоНомеру, Записать) — это тяжелая операция.Рассмотрим, как правильно организовать процесс обработки данных. Нам нужно разделить чтение и обработку на два независимых этапа. Проанализируем пример кода, где мы сначала собираем данные, сворачиваем их, а затем работаем с объектами базы данных:
// Этап 1: Сбор данных
ТЗ_Заказы = Новый ТаблицаЗначений;
ТЗ_Заказы.Колонки.Добавить("НомерЗаказа", Новый ОписаниеТипов("Строка"));
ТЗ_Заказы.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Для НомерСтроки = 2 По Высота Цикл
Стр = ТЗ_Заказы.Добавить();
Стр.НомерЗаказа = СокрЛП(ОбластьНомер.Текст);
Стр.Сумма = Число(ОбластьСумма.Текст);
КонецЦикла;
// Этап 2: Группировка
ТЗ_Заказы.Свернуть("НомерЗаказа", "Сумма");
// Этап 3: Обработка объектов БД
Для Каждого ТекущийЗаказ Из ТЗ_Заказы Цикл
СсылкаНаЗаказ = Документы.ЗаказПокупателя.НайтиПоНомеру(ТекущийЗаказ.НомерЗаказа, ДатаНачалаПоиска);
Если СсылкаНаЗаказ.Пустая() Тогда
Сообщить("Заказ не найден: " + ТекущийЗаказ.НомерЗаказа);
Продолжить;
КонецЕсли;
// Работаем с объектом один раз для всех строк файла по этому заказу
ОбъектЗаказа = СсылкаНаЗаказ.ПолучитьОбъект();
ОбъектЗаказа.СтоимостьДоставки = ТекущийЗаказ.Сумма;
ОбъектЗаказа.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
Если данных очень много (десятки тысяч строк), даже метод Свернуть() может работать медленнее, чем хотелось бы. В таких случаях мы рекомендуем рассмотреть вариант с использованием Временных таблиц в запросе. Вы можете загрузить заполненную таблицу значений (еще не свернутую) в запрос, и с помощью предложения СГРУППИРОВАТЬ ПО получить результат. Запросы в 1С работают на уровне SQL-сервера и оптимизированы для подобных задач гораздо лучше, чем методы коллекции ТаблицаЗначений.
Подведем итог: залог успешной работы метода Свернуть() — это строгая типизация колонок, предварительная очистка строковых данных от пробелов и, самое главное, вызов метода строго после того, как в таблицу добавлены все исходные данные.