Меняет ли порядок строк метод ТЗ.Свернуть() в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с коллекцией ТаблицаЗначений разработчики часто используют метод Свернуть() для быстрого суммирования числовых колонок и удаления дубликатов по заданным ключевым полям. Однако при этом возникает закономерный вопрос: сохраняет ли результирующая таблица исходный порядок строк или платформа перестраивает их по своему усмотрению?

Разберем детально механизм работы данного метода, выясним причину изменения последовательности записей и рассмотрим надежные способы управления порядком строк.

Как работает метод Свернуть() и почему меняется порядок

Проанализируем внутреннее поведение платформы 1С:Предприятие при вызове метода Свернуть(). Для объединения дублирующихся строк и вычисления сумм платформа применяет алгоритм, включающий предварительную внутреннюю сортировку по колонкам группировки. Вследствие этого строки обычно выстраиваются по возрастанию значений ключевых колонок.

Важно понимать: разработчики платформы официально не гарантируют сохранение исходной хронологии записей или стабильность внутренней сортировки после выполнения свертки. Даже если в конкретном релизе или тесте порядок кажется предсказуемым, полагаться на него в рабочей логике конфигурации нельзя.

Посмотрим на пример из вопроса. Предположим, у нас есть исходная таблица значений:


// Создаем структуру и заполняем данные
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Колонка1", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Колонка2", Новый ОписаниеТипов("Число"));

Стр = ТЗ.Добавить();
Стр.Колонка1 = "Строка1";
Стр.Колонка2 = 10;

Стр = ТЗ.Добавить();
Стр.Колонка1 = "Строка2";
Стр.Колонка2 = 11;

Стр = ТЗ.Добавить();
Стр.Колонка1 = "Строка1";
Стр.Колонка2 = 33;

// Выполняем свертку
ТЗ.Свернуть("Колонка1", "Колонка2");

После вызова ТЗ.Свернуть("Колонка1", "Колонка2") мы получим свернутые данные, но рассчитывать на то, что запись «Строка1» обязательно останется перед записью «Строка2», в прикладном коде недопустимо без явного указания правил сортировки.

Ограничения стандартного метода Свернуть()

Прежде чем перейти к вариантам решения, вспомним важные технические особенности штатного метода платформы:

  1. Удаление лишних полей: все колонки, которые не переданы в первом параметре (колонки группировки) или во втором параметре (колонки суммирования), полностью и безвозвратно удаляются из таблицы значений.
  2. Ограничение по типам агрегации: метод умеет выполнять исключительно сложение чисел (тип Число). Получить минимум, максимум, среднее или количество уникальных значений штатными средствами метода невозможно.
  3. Модификация по месту: метод изменяет саму исходную таблицу значений, не создавая новый экземпляр коллекции.

Способ 1. Явная сортировка после вызова метода

Если для бизнес-логики важен конкретный порядок следования (по алфавиту, по возрастанию или убыванию определенных показателей), самым простым решением является вызов метода Сортировать() сразу после свертки.

Разберем по шагам реализацию этого подхода:


// Сворачиваем строки по ключевым колонкам
ТЗ.Свернуть("Колонка1", "Колонка2");

// Явно задаем требуемый порядок строк
ТЗ.Сортировать("Колонка1 Возр");

Этот способ компактен, понятен и гарантирует, что результирующая коллекция всегда упорядочена единообразно на любом релизе платформы.

Способ 2. Сохранение хронологии строк через объект Соответствие

Нередко возникает задача сгруппировать дубликаты, но сохранить исходный порядок первого появления каждой группы в документе или табличной части. Стандартный метод Свернуть() здесь не поможет. Рассмотрим решение через создание новой таблицы и использование объекта Соответствие.

Разберем по шагам программный алгоритм:

  1. Создаем новую результирующую таблицу значений, копируя структуру исходной.
  2. Инициализируем объект Соответствие, в котором ключом будет выступать значение группировочного поля (или структура нескольких полей), а значением — ссылка на созданную строку новой таблицы.
  3. Обходим исходную таблицу циклом. Если ключ еще не встречался в соответствии, создаем новую строку в результирующей таблице, фиксируем ее в соответствии и заполняем поля. Если ключ уже существует — прибавляем сумму к существующей строке.

Посмотрим на пример кода:


// Создаем результирующую таблицу с аналогичной структурой
ТЗ_Результат = ТЗ.СкопироватьКолонки();

// Соответствие для быстрого поиска созданных строк
КэшСтрок = Новый Соответствие;

Для Каждого СтрИсходная Из ТЗ Цикл
    Ключ = СтрИсходная.Колонка1;
    СтрНайденная = КэшСтрок[Ключ];
    
    Если СтрНайденная = Неопределено Тогда
        // Первое появление группы: добавляем строку в результат
        СтрНайденная = ТЗ_Результат.Добавить();
        ЗаполнитьЗначенияСвойств(СтрНайденная, СтрИсходная);
        КэшСтрок.Вставить(Ключ, СтрНайденная);
    Иначе
        // Группа уже существует: накапливаем сумму
        СтрНайденная.Колонка2 = СтрНайденная.Колонка2 + СтрИсходная.Колонка2;
    КонецЕсли;
КонецЦикла;

// В ТЗ_Результат порядок первых вхождений полностью сохранен

Этот подход работает быстро даже на больших объемах данных за счет использования хеш-таблицы объекта Соответствие и полностью сохраняет исходный порядок появления элементов.

Способ 3. Группировка через запрос с сохранением порядка

Еще один универсальный способ — использование механизма запросов. Этот подход особенно удобен, если помимо суммы требуется рассчитать другие агрегатные функции (например, МИНИМУМ, МАКСИМУМ, КОЛИЧЕСТВО) или выполнить сложную сортировку.

Рассмотрим подробнее реализацию через запрос с фиксацией исходного индекса строки:


// Добавляем служебную колонку для фиксации порядка, если ее нет
Если ТЗ.Колонки.Найти("НомерСтроки") = Неопределено Тогда
    ТЗ.Колонки.Добавить("НомерСтроки", Новый ОписаниеТипов("Число"));
    Номер = 0;
    Для Каждого Стр Из ТЗ Цикл
        Номер = Номер + 1;
        Стр.НомерСтроки = Номер;
    КонецЦикла;
КонецЕсли;

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВТ.Колонка1 КАК Колонка1,
    |	СУММА(ВТ.Колонка2) КАК Колонка2,
    |	МИНИМУМ(ВТ.НомерСтроки) КАК ИсходныйПорядок
    |ПОМЕСТИТЬ ВТ_Свернутая
    |ИЗ
    |	&ТаблицаДанных КАК ВТ
    |СГРУППИРОВАТЬ ПО
    |	ВТ.Колонка1
    |;
    |ВЫБРАТЬ
    |	ВТ_Свернутая.Колонка1 КАК Колонка1,
    |	ВТ_Свернутая.Колонка2 КАК Колонка2
    |ИЗ
    |	ВТ_Свернутая КАК ВТ_Свернутая
    |УПОРЯДОЧИТЬ ПО
    |	ВТ_Свернутая.ИсходныйПорядок ВОЗР";

Запрос.УстановитьПараметр("ТаблицаДанных", ТЗ);
ТЗ_Результат = Запрос.Выполнить().Выгрузить();

С помощью агрегатной функции МИНИМУМ(НомерСтроки) мы сохраняем номер первого появления каждой группы и гарантированно восстанавливаем первоначальный порядок записей в секции УПОРЯДОЧИТЬ ПО.

← На главную