При работе с данными в 1С мы часто сталкиваемся с необходимостью агрегировать информацию. Один из типичных сценариев — это группировка строк Таблицы Значений (ТЗ) по определенному критерию, с последующим суммированием числовых полей и объединением строковых значений. Давайте подробно разберем, как эффективно решить эту задачу, используя встроенные возможности платформы 1С.
Представим, что у нас есть Таблица Значений, содержащая информацию о движении по контрагентам. Нам необходимо по каждому уникальному контрагенту получить общую сумму НДС и Оплачено, а также объединить в одну строку все связанные описания, номера документов начисления, электронных накладных и заказов.
Изначальное предложение автора темы, заключающееся в итеративном поиске, суммировании, объединении и удалении строк, хоть и может привести к желаемому результату, является неоптимальным и ресурсоемким, особенно для больших объемов данных. Вместо этого мы рассмотрим более элегантные и производительные подходы, использующие встроенные методы платформы 1С и запросы.
Скопировать, Свернуть, НайтиСтроки, ВыгрузитьКолонку, СтрСоединить)Этот подход является наиболее распространенным и рекомендованным для работы с Таблицами Значений напрямую. Он позволяет выполнить операции группировки и агрегации без привлечения механизмов запросов СУБД, что часто бывает быстрее для умеренных объемов данных, хранящихся уже в оперативной памяти.
Разберем процесс по шагам.
Для начала нам потребуется создать новую Таблицу Значений, которая будет содержать сгруппированные данные. Первым делом мы займемся суммированием числовых колонок. Для этого воспользуемся методом Скопировать() для создания частичной копии исходной ТЗ и методом Свернуть().
Свернуть() позволяет сгруппировать строки по указанным колонкам и выполнить агрегацию (суммирование) по другим указанным колонкам.Рассмотрим пример кода:
// Исходная Таблица Значений (ТЗ) уже заполнена
// Предположим, что она содержит колонки: Контрагент, Оплачено, НДС, Описание, ДокНачисления, Элект.Накладная, Заказ
// 1. Создаем новую Таблицу Значений для агрегированных данных.
// Скопируем только те колонки, которые будут использоваться для группировки и суммирования.
ИтоговаяТЗ = ТЗ.Скопировать(,"Контрагент,Оплачено,НДС");
// 2. Выполняем свертку: группируем по "Контрагент", суммируем "Оплачено" и "НДС".
ИтоговаяТЗ.Свернуть("Контрагент", "Оплачено,НДС");
В результате выполнения этих строк в ИтоговаяТЗ мы получим по одной строке для каждого уникального контрагента, при этом колонки "Оплачено" и "НДС" будут содержать суммарные значения.
Теперь, когда числовые данные сгруппированы, нам необходимо объединить строковые колонки. Это потребует дополнительного прохода по уже свернутой таблице и поиска соответствующих строк в исходной ТЗ.
ИтоговаяТЗ необходимо добавить новые колонки для объединенных строковых значений, если они еще не были созданы.ИтоговаяТЗ, которая представляет собой уникального контрагента.ИтоговаяТЗ мы находим все соответствующие строки в исходной Таблице Значений, используя метод НайтиСтроки().СтрСоединить().ИтоговаяТЗ.Продолжим наш пример кода:
// 3. Добавляем колонки для объединенных строковых значений в ИтоговуюТЗ.
// Если эти колонки уже были в исходной ТЗ и не были исключены при Скопировать, этот шаг может быть пропущен,
// но лучше явно добавить, чтобы контролировать структуру.
ИтоговаяТЗ.Колонки.Добавить("Описание");
ИтоговаяТЗ.Колонки.Добавить("ДокНачисления");
ИтоговаяТЗ.Колонки.Добавить("ЭлектНакладная"); // Уточняем имя колонки, если оно отличается
ИтоговаяТЗ.Колонки.Добавить("Заказ");
// Создаем структуру для отбора по контрагенту
Отбор = Новый Структура("Контрагент");
// 4. Итерируем по каждой строке свернутой таблицы (ИтоговаяТЗ).
Для Каждого СтрИзИтоговойТЗ Из ИтоговаяТЗ Цикл
// Заполняем структуру отбора значением контрагента из текущей строки ИтоговойТЗ
ЗаполнитьЗначенияСвойств(Отбор, СтрИзИтоговойТЗ);
// Находим все строки в исходной ТЗ, соответствующие текущему контрагенту
СтрокиПоКонтрагенту = ТЗ.НайтиСтроки(Отбор);
// 5. Извлекаем значения колонки "Описание" из найденных строк и объединяем их.
// Сначала создаем копию найденных строк, содержащую только колонку "Описание".
// Затем выгружаем эту колонку в массив и объединяем элементы массива.
Если СтрокиПоКонтрагенту.Количество() > 0 Тогда
МассивОписаний = ТЗ.Скопировать(СтрокиПоКонтрагенту, "Описание").ВыгрузитьКолонку("Описание");
СтрИзИтоговойТЗ.Описание = СтрСоединить(МассивОписаний, "; ");
МассивДокНачислений = ТЗ.Скопировать(СтрокиПоКонтрагенту, "ДокНачисления").ВыгрузитьКолонку("ДокНачисления");
СтрИзИтоговойТЗ.ДокНачисления = СтрСоединить(МассивДокНачислений, "; ");
МассивЭлектНакладных = ТЗ.Скопировать(СтрокиПоКонтрагенту, "Элект.Накладная").ВыгрузитьКолонку("Элект.Накладная"); // Уточняем имя
СтрИзИтоговойТЗ.ЭлектНакладная = СтрСоединить(МассивЭлектНакладных, "; ");
МассивЗаказов = ТЗ.Скопировать(СтрокиПоКонтрагенту, "Заказ").ВыгрузитьКолонку("Заказ");
СтрИзИтоговойТЗ.Заказ = СтрСоединить(МассивЗаказов, "; ");
КонецЕсли;
КонецЦикла;
Таким образом, мы сначала эффективно суммируем числовые значения с помощью метода Свернуть(), а затем, пройдясь по уже сгруппированным данным, аккуратно собираем и объединяем строковые значения. Этот подход является гибким и достаточно производительным для многих задач.
Преимущества:
Свернуть(), НайтиСтроки(), ВыгрузитьКолонку() реализованы на низком уровне и оптимизированы платформой, что делает их очень быстрыми.Недостатки:
Несмотря на дискуссии в сообщении 9, запросы 1С являются мощным инструментом для обработки данных, в том числе и для Таблиц Значений. Для больших объемов данных или при сложных условиях группировки использование запросов может быть более предпочтительным.
Применение запроса к Таблице Значений осуществляется путем передачи ТЗ в запрос в качестве параметра и обращения к ней как к виртуальной таблице.
Для работы с Таблицей Значений в запросе мы используем механизм временных таблиц. Исходная ТЗ передается в запрос как параметр.
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ИсходнаяТЗ", ТЗ); // ТЗ - это наша исходная Таблица Значений
В запросе можно легко использовать оператор СГРУППИРОВАТЬ ПО и агрегатные функции СУММА() для числовых полей. Этот подход часто бывает производительнее для больших объемов данных, поскольку оптимизации выполняются на уровне СУБД (или внутренней реализации 1С для временных таблиц).
Пример запроса для суммирования:
Запрос.Текст =
"ВЫБРАТЬ
| ИсходнаяТЗ.Контрагент КАК Контрагент,
| СУММА(ИсходнаяТЗ.Оплачено) КАК Оплачено,
| СУММА(ИсходнаяТЗ.НДС) КАК НДС
|ИЗ
| &ИсходнаяТЗ КАК ИсходнаяТЗ
|СГРУППИРОВАТЬ ПО
| ИсходнаяТЗ.Контрагент";
РезультатЗапроса = Запрос.Выполнить().Выгрузить(); // Получаем свернутую ТЗ с суммами
Переменная РезультатЗапроса теперь будет содержать Таблицу Значений с уникальными контрагентами и просуммированными колонками "Оплачено" и "НДС".
GROUP_CONCAT)В стандартных запросах 1С (до версии 8.3.16) нет прямой аналогии функции GROUP_CONCAT (как в SQL). Это означает, что для объединения строковых колонок только средствами запроса потребуется более сложная логика, часто с использованием временных таблиц и соединения строк в цикле внутри самого запроса, или, что чаще, комбинация запроса и последующей обработки на встроенном языке.
Для версий 1С 8.3.16 и выше появилась возможность агрегации строк в запросах с помощью специализированных функций. Если ваша платформа поддерживает эту функциональность, вы можете использовать ее для более полного решения проблемы в одном запросе. Однако, для обеспечения совместимости с более ранними версиями или при отсутствии специфических функций, часто возвращаются к подходу из Метода 1 для строковых полей:
РезультатЗапроса) будет аналогичен ИтоговаяТЗ из первого метода.РезультатЗапроса и для каждой строки выполнить поиск в исходной ТЗ (ТЗ.НайтиСтроки()) и объединить строковые колонки с помощью СтрСоединить(), как это было описано в Шаге 2 первого метода.Этот гибридный подход сочетает эффективность запросов для числовой агрегации с гибкостью методов Таблицы Значений для строковых полей.
Пример кода гибридного подхода (продолжение):
// ... код для выполнения запроса и получения РезультатЗапроса ...
// Добавляем колонки для строковых значений в РезультатЗапроса.
РезультатЗапроса.Колонки.Добавить("Описание");
РезультатЗапроса.Колонки.Добавить("ДокНачисления");
РезультатЗапроса.Колонки.Добавить("ЭлектНакладная");
РезультатЗапроса.Колонки.Добавить("Заказ");
Отбор = Новый Структура("Контрагент");
Для Каждого СтрИзРезультата Запроса Из РезультатЗапроса Цикл
ЗаполнитьЗначенияСвойств(Отбор, СтрИзРезультатаЗапроса);
СтрокиПоКонтрагенту = ТЗ.НайтиСтроки(Отбор); // Поиск в исходной ТЗ
Если СтрокиПоКонтрагенту.Количество() > 0 Тогда
СтрИзРезультатаЗапроса.Описание = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "Описание").ВыгрузитьКолонку("Описание"), "; ");
СтрИзРезультатаЗапроса.ДокНачисления = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "ДокНачисления").ВыгрузитьКолонку("ДокНачисления"), "; ");
СтрИзРезультатаЗапроса.ЭлектНакладная = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "Элект.Накладная").ВыгрузитьКолонку("Элект.Накладная"), "; ");
СтрИзРезультатаЗапроса.Заказ = СтрСоединить(ТЗ.Скопировать(СтрокиПоКонтрагенту, "Заказ").ВыгрузитьКолонку("Заказ"), "; ");
КонецЕсли;
КонецЦикла;
Преимущества:
Недостатки:
GROUP_CONCAT в большинстве версий 1С (до 8.3.16) усложняет получение объединенных строк одним запросом, требуя гибридного подхода или специфических функций платформы.Свернуть, НайтиСтроки и т.д.) часто является более простым и достаточно эффективным. Для больших объемов данных рассмотрите гибридный подход с запросом для суммирования и последующей обработкой строковых полей методами ТЗ.Сортировать() перед выгрузкой колонки).Мы рассмотрели два основных эффективных способа группировки и объединения строк Таблицы Значений в 1С. Выбор конкретного метода зависит от объема данных, требований к производительности и версии платформы. В большинстве случаев комбинация методов Свернуть() для числовых полей и последующей обработки строковых колонок является оптимальным решением.