При разработке отчетов и обработок в 1С часто возникает задача, когда необходимо вывести в одну строку данные главного документа, а в отдельной колонке сформировать список или коллекцию всех подчиненных ему документов (документов-оснований). Если мы используем обычное левое соединение в запросе, то строки с главным документом дублируются. Давайте разберем, как получить красивую таблицу, где в поле Документы будет содержаться коллекция ссылок на подчиненные документы.
Важно сразу прояснить ограничения платформы 1С: получить произвольную вложенную коллекцию (которая не является табличной частью) непосредственно в тексте запроса с помощью оператора ВЫБРАТЬ невозможно. Табличные части — это единственная возможность получить вложенную коллекцию в чистых результатах запроса. Для всех остальных случаев мы рассмотрим эффективные обходные пути.
Если конечная цель — вывод данных пользователю или использование механизмов программного формирования отчета, то СКД справляется с этой задачей "из коробки". Разберем доступные функции.
1. Функция Массив()
В выражениях ресурсов СКД мы можем использовать агрегатную функцию Массив(). Она позволяет собрать все сгруппированные значения в настоящий объект платформы.
Массив(Документ2).Документ1.При выгрузке результата такого отчета в таблицу значений в колонке ресурсов мы получим объекты типа Массив.
2. Функция СоединитьСтроки()
Если задача заключается только в визуализации списка документов для пользователя, гораздо выгоднее соединить их представления в одну текстовую строку. Для этого в ресурсах СКД используем функцию СоединитьСтроки():
СоединитьСтроки(Документ2, ", ")
Это выражение аккуратно схлопнет все документы через запятую, избавляя нас от необходимости писать циклы перебора записей вручную.
Если мы работаем вне СКД (например, в модуле объекта при проведении или в серверном коде обработки), рассмотрим классический подход с использованием итогов.
В тексте запроса мы группируем данные по главному документу с помощью секции ИТОГИ. Проанализируем этот алгоритм:
ИТОГИ ПО Документ1.ДеревоЗначений с помощью метода Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам).В результате мы получаем дерево, где на первом (верхнем) уровне располагаются данные главного документа (Дата, Контрагент, Сумма), а на нижнем (подчиненном) уровне — детальные записи с подчиненными документами. Далее дерево можно обойти циклом и собрать из строк нужные коллекции или таблицы.
Использование деревьев значений отлично работает на тысячах строк. Но что делать, если в таблице больше 1 миллиона записей? Обход всех строк дерева и суммирование займут неприемлемо большое время, так как работа с вложенными строками дерева в памяти требует значительных ресурсов.
Разберем наиболее производительный метод для огромных массивов данных — сборку плоской таблицы значений и использование объекта Соответствие.
ТаблицаЗначений.Соответствие, которое будет выступать в роли быстрого кэша, где ключ — это ссылка на Документ1, а значение — массив подчиненных документов.Посмотрим на пример реализации данного алгоритма:
// Предполагаем, что РезультатЗапроса - это плоская таблица
ТаблицаДанных = РезультатЗапроса.Выгрузить();
КэшПодчиненных = Новый Соответствие;
ИтоговаяТаблица = Новый ТаблицаЗначений;
ИтоговаяТаблица.Колонки.Добавить("Документ1");
ИтоговаяТаблица.Колонки.Добавить("МассивДокументов2");
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
// Ищем массив в соответствии
МассивДок2 = КэшПодчиненных.Получить(СтрокаТЗ.Документ1);
Если МассивДок2 = Неопределено Тогда
// Если документа еще нет, создаем массив и новую строку в итоговой таблице
МассивДок2 = Новый Массив;
КэшПодчиненных.Вставить(СтрокаТЗ.Документ1, МассивДок2);
НоваяСтрокаИтогов = ИтоговаяТаблица.Добавить();
НоваяСтрокаИтогов.Документ1 = СтрокаТЗ.Документ1;
НоваяСтрокаИтогов.МассивДокументов2 = МассивДок2;
КонецЕсли;
// Добавляем подчиненный документ в массив, если он не пустой (Null при левом соединении)
Если ЗначениеЗаполнено(СтрокаТЗ.Документ2) Тогда
МассивДок2.Добавить(СтрокаТЗ.Документ2);
КонецЕсли;
КонецЦикла;
Проанализируем преимущества такого подхода: поиск по ключу в объекте Соответствие (хэш-таблица) происходит практически мгновенно. Мы обходим выборку из миллионов строк всего за один линейный проход (сложность O(n)). При этом в оперативную память не загружается тяжелая структура дерева, и мы получаем готовую плоскую ТаблицаЗначений, в одной из колонок которой лежат сформированные массивы. Данную таблицу затем можно индексировать или передавать для дальнейшей обработки.
Нюансы управления памятью: Обратите внимание, что хранение миллионов независимых объектов типа Массив в памяти сеанса может потребовать большого объема ОЗУ (особенно в 32-битных рабочих процессах). Если вы работаете с экстремальными объемами, рассмотрите возможность выполнения расчетов порциями или использования временных таблиц базы данных в пакете запросов, где соединения осуществляются лишь в момент финальной записи, минуя конвертацию объектов в память сервера 1С.