Как сгруппировать и просуммировать данные в таблице значений 1С?

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

В процессе разработки на платформе 1С часто возникает задача обработки данных, находящихся во временных коллекциях, таких как ТаблицаЗначений. Одна из самых распространенных операций — это агрегация данных: группировка по одному или нескольким полям с одновременным суммированием числовых показателей. Например, у нас есть таблица с продажами, где указан товар и сумма по каждой строке документа, а нам нужно получить итоговую сумму по каждому уникальному товару. Рассмотрим, как эффективно решить эту задачу несколькими способами.

Проанализируем ситуацию на простом примере. Предположим, у нас есть ТаблицаЗначений со следующими колонками и данными:

Наша цель — получить новую или преобразовать существующую таблицу к следующему виду, где строки сгруппированы по колонке "Показатель", а значения в колонке "Значение" просуммированы:

Разберем три основных способа, как этого можно достичь в 1С.

Способ 1: Использование метода Свернуть()

Это самый простой, быстрый и предназначенный именно для этой задачи метод объекта ТаблицаЗначений. Метод Свернуть() "схлопывает" таблицу по указанным колонкам, суммируя значения в других указанных числовых колонках.

Синтаксис метода прост: ТаблицаЗначений.Свернуть(ГруппировочныеКолонки, СуммируемыеКолонки).

  1. ГруппировочныеКолонки — строка с именами колонок, по которым нужно сгруппировать данные, перечисленными через запятую.
  2. СуммируемыеКолонки — строка с именами числовых колонок, значения в которых нужно просуммировать.

Посмотрим на пример кода для нашей задачи:


// Предположим, у нас есть переменная ИсходнаяТЗ
// типа ТаблицаЗначений, уже заполненная данными.

ИсходнаяТЗ.Свернуть("Показатель", "Значение");

// После выполнения этой строки в таблице ИсходнаяТЗ
// останутся только сгруппированные и просуммированные данные.

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

Вот как это сделать правильно:


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

// Применяем метод Свернуть() уже к копии
РезультатТЗ.Свернуть("Показатель", "Значение");

// Теперь у нас есть две таблицы:
// ИсходнаяТЗ - с первоначальными данными.
// РезультатТЗ - со сгруппированными итогами.

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

Способ 2: Использование Запроса к Таблице Значений

Когда требуется большая гибкость, например, использовать другие агрегатные функции (найти среднее, максимум, количество уникальных записей), наложить сложные условия или объединить данные с другими таблицами, на помощь приходит язык запросов 1С. Мы можем передать нашу ТаблицаЗначений в запрос в качестве временной таблицы-параметра.

Разберем по шагам, как это работает:

  1. Создаем новый объект Запрос.
  2. В тексте запроса обращаемся к нашей таблице через параметр (например, &ТЗ).
  3. Используем стандартные операторы SQL-подобного языка 1С: ВЫБРАТЬ, СУММА(), СГРУППИРОВАТЬ ПО.
  4. Устанавливаем значение параметра запроса, передавая нашу таблицу.
  5. Выполняем запрос и выгружаем результат в новую таблицу значений.

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


// Создаем и настраиваем запрос
Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    ДанныеТЗ.Показатель,
    |    СУММА(ДанныеТЗ.Значение) КАК Значение
    |ИЗ
    |    &НашаТаблица КАК ДанныеТЗ
    |
    |СГРУППИРОВАТЬ ПО
    |    ДанныеТЗ.Показатель";

// Передаем нашу таблицу значений в параметр запроса
Запрос.УстановитьПараметр("НашаТаблица", ИсходнаяТЗ);

// Выполняем запрос и получаем результат
РезультатВыполнения = Запрос.Выполнить();

// Выгружаем результат в новую таблицу значений
РезультатТЗ = РезультатВыполнения.Выгрузить();

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


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

Способ 3: Ручная группировка через обход коллекции

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

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

Рассмотрим пример с использованием объекта Соответствие:


// 1. Создаем соответствие для хранения промежуточных итогов
// Ключ - Показатель, Значение - накопленная сумма
СуммыПоПоказателям = Новый Соответствие;

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

// 3. Теперь нужно выгрузить результат из Соответствия в новую Таблицу Значений
РезультатТЗ = Новый ТаблицаЗначений;
РезультатТЗ.Колонки.Добавить("Показатель");
РезультатТЗ.Колонки.Добавить("Значение");

Для Каждого ЭлементКоллекции Из СуммыПоПоказателям Цикл
    НоваяСтрока = РезультатТЗ.Добавить();
    НоваяСтрока.Показатель = ЭлементКоллекции.Ключ;
    НоваяСтрока.Значение = ЭлементКоллекции.Значение;
КонецЦикла;

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

Сравнение методов: что и когда использовать?

Подведем итог и определим, в каких ситуациях какой метод предпочтительнее.

  1. Свернуть()
    • Когда использовать: Для простых и стандартных задач группировки с суммированием.
    • Плюсы: Максимально простой и лаконичный код, высокая производительность на большинстве операций.
    • Минусы: Ограничен только суммированием, изменяет исходную таблицу (требует создания копии, если исходник нужен).
  2. Запрос
    • Когда использовать: Для сложных агрегаций (среднее, количество, максимум), при необходимости наложить условия отбора (ГДЕ), объединить данные из нескольких источников.
    • Плюсы: Невероятная гибкость и мощность, не изменяет исходную таблицу, часто оказывается самым производительным решением на очень больших объемах данных.
    • Минусы: Код более громоздкий по сравнению со Свернуть().
  3. Ручной обход
    • Когда использовать: В редких, нестандартных случаях, когда требуется полностью кастомная логика агрегации, которую нельзя выразить через стандартные функции.
    • Плюсы: Полный контроль над процессом.
    • Минусы: Много кода, сложность в чтении и поддержке, как правило, самая низкая производительность.

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

← На главную