Как получить итог в запросе сразу по нескольким полям без построения сложной иерархии

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

При работе с запросами в 1С разработчики часто сталкиваются с тем, что стандартная конструкция ИТОГИ навязывает жесткую иерархическую структуру. Если нам нужно получить итоги по нескольким измерениям (например, по Складу, Товару и Организации), система по умолчанию строит «дерево», где каждое следующее измерение является подчиненным предыдущему. Однако на практике часто требуется получить «плоскую» таблицу или итоги в разрезе нескольких полей одновременно, не заставляя пользователя раскрывать бесконечные уровни группировок.

В этой статье мы подробно разберем, как обойти это ограничение, проанализируем различные подходы — от динамического формирования текста запроса до использования механизмов Системы компоновки данных (СКД) и программной обработки таблиц значений.

Проблема стандартных итогов в запросе

Рассмотрим стандартную ситуацию. Когда мы пишем в запросе ИТОГИ Сумма(Остаток) ПО Измерение1, Измерение2, 1С подготавливает результат таким образом, что при обходе по группировкам Измерение2 всегда будет находиться «внутри» Измерение1. Если нам нужно вывести итоги по этим полям в одной строке или получить сводную таблицу (кросс-таблицу), обычный обход результата запроса Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) заставит нас писать сложные вложенные циклы.

Давайте выясним, какие существуют альтернативы, позволяющие сделать отчет более гибким, особенно когда состав измерений заранее неизвестен или может меняться.

Способ 1. Динамическое построение запроса (SQL Pivoting)

Этот метод основан на программном формировании текста запроса. Если мы заранее знаем, какие значения измерений должны стать колонками, мы можем использовать конструкцию ВЫБОР КОГДА. Это позволяет «развернуть» строки в колонки прямо на уровне СУБД.

Проанализируем пример кода, который динамически формирует колонки для итогов:


// Предположим, у нас есть массив нужных измерений
ТекстВыбора = "";
Для Каждого ТекущееИзмерение Из СписокИзмерений Цикл
    ТекстВыбора = ТекстВыбора + 
        ", СУММА(ВЫБОР КОГДА ВТ_Данные.Измерение = &Значение" + ТекущееИзмерение.Имя + 
        " ТОГДА ВТ_Данные.Сумма ИНАЧЕ 0 КОНЕЦ) КАК " + ТекущееИзмерение.Имя;
КонецЦикла;

ТекстЗапроса = "ВЫБРАТЬ ОбщийРазрез" + ТекстВыбора + " ИЗ ВТ_Данные КАК ВТ_Данные СГРУППИРОВАТЬ ПО ОбщийРазрез";

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

Способ 2. Использование СКД с внешним набором данных

Многие программисты стараются избегать СКД из-за необходимости «рисовать» схему в конфигураторе. Однако мы можем подойти к задаче иначе: использовать мощь движка компоновки данных программно. Это позволит нам не переделывать схему вручную при изменении измерений в регистрах.

Рассмотрим последовательность действий:

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

Этот способ является наиболее универсальным, так как СКД берет на себя всю рутину по расчету иерархических и общих итогов. Для упрощения написания подобного кода подойдёт модуль-обёртка для программного создания СКД.

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

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


// ТаблицаЗначений содержит колонки: Измерение1, Измерение2, Измерение3, Сумма
// Нам нужно получить итоги в разрезе Измерение1 и Измерение3, проигнорировав Измерение2

КолонкиГруппировки = "Измерение1, Измерение3";
КолонкиСуммирования = "Сумма";

ТаблицаИтогов = ИсходнаяТаблица.Скопировать();
ТаблицаИтогов.Свернуть(КолонкиГруппировки, КолонкиСуммирования);

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

Способ 4. Конструкция ИТОГИ ПО ОБЩИЕ в запросе

Вернемся к запросам. Мало кто знает, что конструкция ИТОГИ позволяет получать итоги по полям независимо друг от друга, если правильно использовать ключевое слово ОБЩИЕ. Разберем пример:


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

Проанализируем результат такого запроса. При обходе РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Склад") вы получите итоги только по складам. Если вы вызовете РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура"), вы получите итоги по всей номенклатуре без привязки к складам (как если бы склада в запросе не было вообще). Это позволяет в рамках одного запроса собрать данные для разных итоговых таблиц, не строя вложенных деревьев.

Способ 5. Универсальный отчет по метаданным

Если задача стоит максимально широко — «сделать отчет по любому регистру», — стоит обратить внимание на механизмы типовых конфигураций (например, БСП). В них встроен «Универсальный отчет», который умеет работать с любым регистром на основе его метаданных.

Если вы пишете свое решение, вы можете программно перебирать коллекцию Метаданные.РегистрыНакопления[ИмяРегистра].Измерения. Это позволит вам:

Резюме и рекомендации

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

  1. Если нужно быстро получить итоги в коде для небольшого объема данных — используйте ТаблицаЗначений.Свернуть().
  2. Если требуется сложный отчет с пользовательскими настройками, но структура регистров меняется — используйте программное формирование СКД через внешний набор данных.
  3. Если нужно «развернуть» конкретные значения в колонки — применяйте динамический запрос с ВЫБОР КОГДА.
  4. Если необходимо получить данные по нескольким полям независимо в одном запросе — используйте ИТОГИ ПО ОБЩИЕ, Поле1, Поле2 и правильные параметры метода Выбрать().

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

← На главную