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

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

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

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

Изначальное предложение автора темы, заключающееся в итеративном поиске, суммировании, объединении и удалении строк, хоть и может привести к желаемому результату, является неоптимальным и ресурсоемким, особенно для больших объемов данных. Вместо этого мы рассмотрим более элегантные и производительные подходы, использующие встроенные методы платформы 1С и запросы.

Метод 1: Использование методов Таблицы Значений (Скопировать, Свернуть, НайтиСтроки, ВыгрузитьКолонку, СтрСоединить)

Этот подход является наиболее распространенным и рекомендованным для работы с Таблицами Значений напрямую. Он позволяет выполнить операции группировки и агрегации без привлечения механизмов запросов СУБД, что часто бывает быстрее для умеренных объемов данных, хранящихся уже в оперативной памяти.

Разберем процесс по шагам.

Шаг 1: Подготовка и свертка числовых колонок

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

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

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


// Исходная Таблица Значений (ТЗ) уже заполнена
// Предположим, что она содержит колонки: Контрагент, Оплачено, НДС, Описание, ДокНачисления, Элект.Накладная, Заказ

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

// 2. Выполняем свертку: группируем по "Контрагент", суммируем "Оплачено" и "НДС".
ИтоговаяТЗ.Свернуть("Контрагент", "Оплачено,НДС");

В результате выполнения этих строк в ИтоговаяТЗ мы получим по одной строке для каждого уникального контрагента, при этом колонки "Оплачено" и "НДС" будут содержать суммарные значения.

Шаг 2: Объединение строковых колонок

Теперь, когда числовые данные сгруппированы, нам необходимо объединить строковые колонки. Это потребует дополнительного прохода по уже свернутой таблице и поиска соответствующих строк в исходной ТЗ.

  1. Добавляем колонки для строковых значений. В ИтоговаяТЗ необходимо добавить новые колонки для объединенных строковых значений, если они еще не были созданы.
  2. Итерируем по свернутой таблице. Мы будем проходить по каждой строке ИтоговаяТЗ, которая представляет собой уникального контрагента.
  3. Находим исходные строки. Для каждого контрагента из ИтоговаяТЗ мы находим все соответствующие строки в исходной Таблице Значений, используя метод НайтиСтроки().
  4. Извлекаем и объединяем строковые значения. Из найденных строк мы извлекаем значения нужных строковых колонок, формируем из них массив и объединяем в одну строку с помощью функции СтрСоединить().
  5. Записываем результат. Полученную объединенную строку записываем в соответствующую колонку текущей строки ИтоговаяТЗ.

Продолжим наш пример кода:


// 3. Добавляем колонки для объединенных строковых значений в ИтоговуюТЗ.
// Если эти колонки уже были в исходной ТЗ и не были исключены при Скопировать, этот шаг может быть пропущен,
// но лучше явно добавить, чтобы контролировать структуру.
ИтоговаяТЗ.Колонки.Добавить("Описание");
ИтоговаяТЗ.Колонки.Добавить("ДокНачисления");
ИтоговаяТЗ.Колонки.Добавить("ЭлектНакладная"); // Уточняем имя колонки, если оно отличается
ИтоговаяТЗ.Колонки.Добавить("Заказ");

// Создаем структуру для отбора по контрагенту
Отбор = Новый Структура("Контрагент");

// 4. Итерируем по каждой строке свернутой таблицы (ИтоговаяТЗ).
Для Каждого СтрИзИтоговойТЗ Из ИтоговаяТЗ Цикл
    // Заполняем структуру отбора значением контрагента из текущей строки ИтоговойТЗ
    ЗаполнитьЗначенияСвойств(Отбор, СтрИзИтоговойТЗ);
    
    // Находим все строки в исходной ТЗ, соответствующие текущему контрагенту
    СтрокиПоКонтрагенту = ТЗ.НайтиСтроки(Отбор);
    
    // 5. Извлекаем значения колонки "Описание" из найденных строк и объединяем их.
    // Сначала создаем копию найденных строк, содержащую только колонку "Описание".
    // Затем выгружаем эту колонку в массив и объединяем элементы массива.
    Если СтрокиПоКонтрагенту.Количество() > 0 Тогда
        МассивОписаний = ТЗ.Скопировать(СтрокиПоКонтрагенту, "Описание").ВыгрузитьКолонку("Описание");
        СтрИзИтоговойТЗ.Описание = СтрСоединить(МассивОписаний, "; ");
        
        МассивДокНачислений = ТЗ.Скопировать(СтрокиПоКонтрагенту, "ДокНачисления").ВыгрузитьКолонку("ДокНачисления");
        СтрИзИтоговойТЗ.ДокНачисления = СтрСоединить(МассивДокНачислений, "; ");
        
        МассивЭлектНакладных = ТЗ.Скопировать(СтрокиПоКонтрагенту, "Элект.Накладная").ВыгрузитьКолонку("Элект.Накладная"); // Уточняем имя
        СтрИзИтоговойТЗ.ЭлектНакладная = СтрСоединить(МассивЭлектНакладных, "; ");
        
        МассивЗаказов = ТЗ.Скопировать(СтрокиПоКонтрагенту, "Заказ").ВыгрузитьКолонку("Заказ");
        СтрИзИтоговойТЗ.Заказ = СтрСоединить(МассивЗаказов, "; ");
    КонецЕсли;
КонецЦикла;

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

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

Преимущества:

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

Недостатки:

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

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

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

Применение запроса к Таблице Значений осуществляется путем передачи ТЗ в запрос в качестве параметра и обращения к ней как к виртуальной таблице.

Шаг 1: Создание временной таблицы в запросе

Для работы с Таблицей Значений в запросе мы используем механизм временных таблиц. Исходная ТЗ передается в запрос как параметр.


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

Шаг 2: Группировка и суммирование числовых полей

В запросе можно легко использовать оператор СГРУППИРОВАТЬ ПО и агрегатные функции СУММА() для числовых полей. Этот подход часто бывает производительнее для больших объемов данных, поскольку оптимизации выполняются на уровне СУБД (или внутренней реализации 1С для временных таблиц).

Пример запроса для суммирования:


Запрос.Текст = 
    "ВЫБРАТЬ
    |    ИсходнаяТЗ.Контрагент КАК Контрагент,
    |    СУММА(ИсходнаяТЗ.Оплачено) КАК Оплачено,
    |    СУММА(ИсходнаяТЗ.НДС) КАК НДС
    |ИЗ
    |    &ИсходнаяТЗ КАК ИсходнаяТЗ
    |СГРУППИРОВАТЬ ПО
    |    ИсходнаяТЗ.Контрагент";

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

Переменная РезультатЗапроса теперь будет содержать Таблицу Значений с уникальными контрагентами и просуммированными колонками "Оплачено" и "НДС".

Шаг 3: Объединение строковых полей (эмуляция GROUP_CONCAT)

В стандартных запросах 1С (до версии 8.3.16) нет прямой аналогии функции GROUP_CONCAT (как в SQL). Это означает, что для объединения строковых колонок только средствами запроса потребуется более сложная логика, часто с использованием временных таблиц и соединения строк в цикле внутри самого запроса, или, что чаще, комбинация запроса и последующей обработки на встроенном языке.

Для версий 1С 8.3.16 и выше появилась возможность агрегации строк в запросах с помощью специализированных функций. Если ваша платформа поддерживает эту функциональность, вы можете использовать ее для более полного решения проблемы в одном запросе. Однако, для обеспечения совместимости с более ранними версиями или при отсутствии специфических функций, часто возвращаются к подходу из Метода 1 для строковых полей:

  1. Выполнить запрос на группировку и суммирование числовых полей (как показано выше).
  2. Результат запроса (РезультатЗапроса) будет аналогичен ИтоговаяТЗ из первого метода.
  3. Затем пройтись по РезультатЗапроса и для каждой строки выполнить поиск в исходной ТЗ (ТЗ.НайтиСтроки()) и объединить строковые колонки с помощью СтрСоединить(), как это было описано в Шаге 2 первого метода.

Этот гибридный подход сочетает эффективность запросов для числовой агрегации с гибкостью методов Таблицы Значений для строковых полей.

Пример кода гибридного подхода (продолжение):


// ... код для выполнения запроса и получения РезультатЗапроса ...

// Добавляем колонки для строковых значений в РезультатЗапроса.
РезультатЗапроса.Колонки.Добавить("Описание");
РезультатЗапроса.Колонки.Добавить("ДокНачисления");
РезультатЗапроса.Колонки.Добавить("ЭлектНакладная");
РезультатЗапроса.Колонки.Добавить("Заказ");

Отбор = Новый Структура("Контрагент");

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

    Если СтрокиПоКонтрагенту.Количество() > 0 Тогда
        СтрИзРезультатаЗапроса.Описание = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "Описание").ВыгрузитьКолонку("Описание"), "; ");
        СтрИзРезультатаЗапроса.ДокНачисления = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "ДокНачисления").ВыгрузитьКолонку("ДокНачисления"), "; ");
        СтрИзРезультатаЗапроса.ЭлектНакладная = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "Элект.Накладная").ВыгрузитьКолонку("Элект.Накладная"), "; ");
        СтрИзРезультатаЗапроса.Заказ = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "Заказ").ВыгрузитьКолонку("Заказ"), "; ");
    КонецЕсли;
КонецЦикла;

Преимущества и недостатки метода с использованием запросов:

Преимущества:

  1. Высокая производительность для больших объемов данных: Запросы оптимизированы для работы с большими наборами данных и могут быть значительно быстрее для операций суммирования.
  2. Единообразие подхода: Если вы уже используете запросы для других задач, этот подход будет логичным продолжением.

Недостатки:

  1. Сложность объединения строк: Отсутствие прямой функции GROUP_CONCAT в большинстве версий 1С (до 8.3.16) усложняет получение объединенных строк одним запросом, требуя гибридного подхода или специфических функций платформы.
  2. Дополнительные шаги: Требуется выгрузка ТЗ в параметр запроса и затем выгрузка результата запроса обратно в ТЗ.

Дополнительные рекомендации

  1. Выбор метода: Для небольших и средних Таблиц Значений (до нескольких тысяч строк) подход с методами ТЗ (Свернуть, НайтиСтроки и т.д.) часто является более простым и достаточно эффективным. Для больших объемов данных рассмотрите гибридный подход с запросом для суммирования и последующей обработкой строковых полей методами ТЗ.
  2. Оптимизация: Всегда стремитесь использовать встроенные высокооптимизированные методы платформы 1С. Утверждения типа "без цикла" часто подразумевают использование именно таких методов, которые внутри себя реализуют итерации, но делают это на низком уровне, обеспечивая лучшую производительность по сравнению с явными циклами на встроенном языке.
  3. Чистота кода: Создание новой Таблицы Значений и последовательное заполнение её сгруппированными и объединенными данными, вместо модификации исходной таблицы, является более чистым, безопасным и предсказуемым подходом.
  4. Порядок следования: Если важен порядок следования строк в объединенных строковых полях, убедитесь, что при извлечении данных для объединения этот порядок либо сохраняется, либо данные предварительно сортируются (например, с помощью метода Сортировать() перед выгрузкой колонки).

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

← На главную