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