При построении многоуровневых отчетов в 1С:Предприятии 7.7 часто возникает задача вывести в заголовке родительской группировки точное количество входящих в нее дочерних элементов следующего уровня. Например, для каждой номенклатуры требуется указать число ее партий, а для каждой партии — число адресов хранения.
Разберем, почему стандартные средства запроса не дают нужного результата, и рассмотрим рабочие способы реализации этой логики штатными механизмами платформы.
Почему встроенная функция Счётчик() не подходит?
Агрегатная функция встроенного языка запросов Счётчик() всегда считает количество атомарных физических записей источника данных (строк движений регистров или документов), вошедших в выборку. При сворачивании на верхних уровнях иерархии эти значения автоматически суммируются. Конструкция COUNT(DISTINCT) во встроенном языке запросов 1С 7.7 отсутствует, поэтому получить количество узлов следующего уровня группировки внутри самого текста запроса невозможно.
Проанализируем практические варианты решения этой задачи при выводе отчета.
Наиболее компактный и быстрый способ решения задачи при прямом выводе в печатную форму. Сначала выводим секцию родительской группировки, фиксируем номер текущей строки таблицы, затем в цикле подчиненной группировки выводим строки и инкрементируем счетчик. После завершения вложенного цикла значение счетчика записывается прямо в ячейку ранее выведенной строки.
Рассмотрим пример кода:
Пока Запрос.Группировка(1) = 1 Цикл
Таб.ВывестиСекцию("Группировка1");
ТекСтрока = Таб.ВысотаТаблицы();
Сч = 0;
Пока Запрос.Группировка(2) = 1 Цикл
Таб.ВывестиСекцию("Группировка2");
Сч = Сч + 1;
КонецЦикла;
// Записываем полученное количество в нужную колонку родительской секции
Таб.Область(ТекСтрока, 3, ТекСтрока, 3).Текст = Строка(Сч);
КонецЦикла;
Преимущества: простота реализации, минимальный расход оперативной памяти и отсутствие необходимости повторно обращаться к источнику данных.
Если требуется вывести количество дочерних записей в шапку группировки до вывода подчиненных строк без прямого изменения ячеек макета, можно применить предварительный прогон подчиненного цикла с последующим возвратом указателя выборки с помощью метода Получить().
Разберем по шагам логику этого алгоритма:
Запрос.Получить().Посмотрим на пример кода:
Пока Запрос.Группировка(1) = 1 Цикл
Сч = 0;
Пока Запрос.Группировка(2) = 1 Цикл
Сч = Сч + 1;
КонецЦикла;
// Передаем переменную Сч в параметры секции макета
Таб.ВывестиСекцию("Группировка1");
// Возвращаем курсор на текущую ветку группировки
Запрос.Получить(Запрос.Номенклатура);
Пока Запрос.Группировка(2) = 1 Цикл
Таб.ВывестиСекцию("Группировка2");
КонецЦикла;
КонецЦикла;
Важная особенность метода: при трехуровневой и более глубокой структуре группировок для корректного позиционирования в метод Запрос.Получить() необходимо передавать значения всех вышестоящих измерений, иначе указатель на подчиненные уровни сбросится.
Если отчет сложный и данные требуют многократных манипуляций, эффективным решением будет выгрузка результата запроса в плоскую ТаблицаЗначений с последующей группировкой.
Рассмотрим последовательность действий:
ТаблицаЗначений с помощью команды Запрос.Выгрузить(ТЗ, 0, 0).ТЗКопия.Свернуть("Номенклатура,Партия", "")).Счетчик, заполняем ее единицами и сворачиваем таблицу повторно: ТЗКопия.Свернуть("Номенклатура", "Счетчик").Счетчик содержит точное количество подчиненных партий для каждого товара.Посмотрим на реализацию в коде:
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ, 0, 0);
ТЗПодсчет = СоздатьОбъект("ТаблицаЗначений");
ТЗ.Выгрузить(ТЗПодсчет);
// Оставляем только уникальные комбинации Номенклатура-Партия
ТЗПодсчет.Свернуть("Номенклатура, Партия", "");
ТЗПодсчет.НоваяКолонка("КоличествоПартий", "Число");
ТЗПодсчет.Заполнить(1, 1, ТЗПодсчет.КоличествоСтрок(), "КоличествоПартий");
// Получаем итоговое количество партий на каждую номенклатуру
ТЗПодсчет.Свернуть("Номенклатура", "КоличествоПартий");
В ситуациях, когда требуется полностью разделить логику подсчета итераций и вывод данных, формируют два независимых запроса:
Номенклатура, Партия). Количество вхождений второй группировки определяет число подчиненных записей.Номенклатура, Партия, Адрес) и используется для вывода строк нижнего уровня.Для большинства стандартных отчетов оптимальным по простоте и скорости работы остается Способ 1 с использованием метода Таб.Область().