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