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

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

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

Анализ проблемы: почему стандартный запрос не выдает «таблицу в ячейке»

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


ВЫБРАТЬ
    ЗаказыМатериаловВПроизводствоОстатки.Номенклатура КАК Материал,
    ЗаказыМатериаловВПроизводствоОстатки.Склад КАК Склад,
    СУММА(ЗаказыМатериаловВПроизводствоОстатки.ЗаказаноОстаток) КАК ЗаказаноОстаток,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЗаказыМатериаловВПроизводствоОстатки.Распоряжение) КАК Распоряжение
ИЗ
    РегистрНакопления.ЗаказыМатериаловВПроизводство.Остатки(...) КАК ЗаказыМатериаловВПроизводствоОстатки
СГРУППИРОВАТЬ ПО
    ЗаказыМатериаловВПроизводствоОстатки.Номенклатура,
    ЗаказыМатериаловВПроизводствоОстатки.Склад

Использование функции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) вернет лишь число — количество уникальных документов. Если же мы просто добавим поле Распоряжение в список СГРУППИРОВАТЬ ПО, система развернет результат в плоскую таблицу, где для каждого документа будет своя строка с материалом. Это происходит потому, что язык запросов 1С базируется на реляционной модели SQL, где результатом всегда является двухмерный массив (строки и колонки) с простыми типами данных в ячейках.

Решение №1: Использование предложения ИТОГИ

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

Разберем по шагам создание такого запроса:

  1. Убираем группировку СГРУППИРОВАТЬ ПО для детальных записей.
  2. Добавляем предложение ИТОГИ в конец запроса.

ВЫБРАТЬ
    ЗаказыМатериаловВПроизводствоОстатки.Номенклатура КАК Материал,
    ЗаказыМатериаловВПроизводствоОстатки.Склад КАК Склад,
    ЗаказыМатериаловВПроизводствоОстатки.ЗаказаноОстаток КАК ЗаказаноОстаток,
    ЗаказыМатериаловВПроизводствоОстатки.Распоряжение КАК Распоряжение
ИЗ
    РегистрНакопления.ЗаказыМатериаловВПроизводство.Остатки КАК ЗаказыМатериаловВПроизводствоОстатки
ИТОГИ
    СУММА(ЗаказаноОстаток)
ПО
    Материал,
    Склад

Проанализируем, как работать с таким результатом в коде 1С. Вместо обычного линейного обхода, мы используем обход по группировкам:


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

Важный момент: использование ИТОГИ не позволяет поместить результат во временную таблицу (ПОМЕСТИТЬ). Это итоговая операция, которая завершает формирование выборки.

Решение №2: Система компоновки данных (СКД) и функция СоединитьСтроки

Если ваша задача — вывести эти данные в отчет, то использование чистого запроса не является оптимальным. В СКД есть мощный встроенный механизм агрегации строк. Выясним, как это реализовать.

Проанализируем алгоритм настройки СКД:

  1. В запросе набора данных выбираем поля как обычно (без группировок).
  2. Переходим на закладку Ресурсы.
  3. Для поля Распоряжение в колонке «Выражение» пишем: СоединитьСтроки(Распоряжение, ", ").
  4. В настройках отчета группируем данные по полю Материал и Склад.

В этом случае СКД автоматически соберет все ссылки на документы, относящиеся к одной группировке, и выведет их в одну ячейку через запятую. Это именно то «сворачивание», которое часто требуется пользователям. Посмотрим на возможности функции СоединитьСтроки(): она позволяет не только перечислять значения, но и задавать формат, разделитель и даже порядок сортировки внутри строки.

Решение №3: Программная обработка через Таблицу Значений

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

Разберем пример кода:


// Выгружаем результат запроса (без группировки) в ТЗ
ТЗ = Запрос.Выполнить().Выгрузить();

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

// Группируем данные вручную (упрощенный пример)
// Можно использовать Соответствие для быстрого поиска строк
// Ключ - Материал + Склад, Значение - СтрокаТЗ

Однако более профессиональным подходом будет использование индексированной Таблицы Значений или предварительного запроса с группировкой, к которому мы потом «подцепим» документы. Рассмотрим подробнее вариант с временными таблицами.

Оптимизация: Использование временных таблиц

Если данных в регистре ЗаказыМатериаловВПроизводство очень много, группировка по всем полям сразу может замедлить работу СУБД. Проанализируем более производительный подход:

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

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


ВЫБРАТЬ
    Остатки.Номенклатура КАК Материал,
    Остатки.Склад КАК Склад,
    СУММА(Остатки.ЗаказаноОстаток) КАК ОбщийОстаток
ПОМЕСТИТЬ ВТ_СуммарныеОстатки
ИЗ
    РегистрНакопления.ЗаказыМатериаловВПроизводство.Остатки КАК Остатки
СГРУППИРОВАТЬ ПО
    Остатки.Номенклатура,
    Остатки.Склад;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Материал,
    ВТ.Склад,
    ВТ.ОбщийОстаток,
    ИсходныеДанные.Распоряжение
ИЗ
    ВТ_СуммарныеОстатки КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыМатериаловВПроизводство.Остатки КАК ИсходныеДанные
    ПО ВТ.Материал = ИсходныеДанные.Номенклатура
    И ВТ.Склад = ИсходныеДанные.Склад

Выводы и рекомендации

Проанализировав все доступные методы, мы можем сделать следующие выводы:

При выборе решения всегда учитывайте объем данных. Если документов тысячи, то агрегация строк в СКД или формирование огромных массивов в памяти может привести к снижению производительности. В таких случаях лучше оставить данные в плоском виде и обрабатывать их порционно.

← На главную