Почему в запросах 1С с итогами и иерархией дублируются группы и как это исправить?

Программист 1С v8.3 (Управляемые формы)
← К списку

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

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

Причины дублирования и общие подходы к решению

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

  1. Особенности вывода иерархии: В 1С, особенно при работе с
    
    Системой Компоновки Данных
    
    (СКД) или при ручном обходе иерархических результатов запросов, родительский элемент может выводиться как заголовок группы, а затем еще раз как самостоятельный элемент, если он сам имеет данные или является частью детальных записей.
  2. Некорректное использование операторов запроса: Операторы типа
    
    В ИЕРАРХИИ
    
    могут включать сам родительский элемент, что в сочетании с группировками приводит к его повторному появлению.
  3. Различия в инструментах отладки:
    
    Консоль запросов
    
    и
    
    Консоль отчетов
    
    (или СКД) могут по-разному обрабатывать и отображать иерархические данные, что иногда сбивает с толку.

Теперь давайте перейдем к конкретным решениям, которые помогут нам избавиться от дублирования.

Решение 1: Оптимизация текста запроса для работы с итогами и иерархией

Первый и самый важный шаг – это правильная формулировка запроса. Мы можем использовать несколько приемов непосредственно в тексте запроса или при его выполнении.

1.1. Использование конструкции

ТОЛЬКО ИЕРАРХИЯ
в запросах с итогами

Если нам нужны итоги только по группам, без включения самих элементов в иерархические строки итогов, следует использовать модификатор


ТОЛЬКО ИЕРАРХИЯ
. Рассмотрим подробнее:

Давайте посмотрим на пример запроса:


ВЫБРАТЬ
    Справочник.Ссылка,
    Справочник.Наименование
ИЗ
    Справочник.МойСправочник КАК Справочник
ИТОГИ ПО
    Справочник.Ссылка ТОЛЬКО ИЕРАРХИЯ

В этом примере, если


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

1.2. Корректное использование операторов

В ИЕРАРХИИ
и

Родитель В ИЕРАРХИИ

Одним из ключевых моментов, который часто вызывает дублирование, является неправильное применение оператора


В ИЕРАРХИИ
в условии выборки. Проанализируем ситуацию:

Один из участников форума, столкнувшийся с проблемой, успешно решил ее, изменив условие с


уатМоделиТС.Ссылка В ИЕРАРХИИ(&Ссылка)
на

уатМоделиТС.Родитель В ИЕРАРХИИ(&Ссылка)
. Это наглядный пример того, как правильное использование поля

Родитель
может устранить дублирование.

Пример использования:


ВЫБРАТЬ
    МоделиТС.Ссылка,
    МоделиТС.Наименование
ИЗ
    Справочник.МоделиТС КАК МоделиТС
ГДЕ
    МоделиТС.Родитель В ИЕРАРХИИ(&ВыбраннаяГруппа)

1.3. Исключение групп из детальных записей

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


НЕ [ИмяСправочника].ЭтоГруппа
в текст запроса.


ВЫБРАТЬ
    Справочник.Ссылка,
    Справочник.Наименование
ИЗ
    Справочник.МойСправочник КАК Справочник
ГДЕ
    НЕ Справочник.ЭтоГруппа

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

Решение 2: Использование Системы Компоновки Данных (СКД)

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

2.1. Преимущества СКД для иерархических отчетов

Рассмотрим, почему СКД так эффективна:

  1. Гибкие настройки группировок: СКД позволяет легко настраивать типы группировок ("Иерархия", "Только иерархия", "Элементы и иерархия") прямо в пользовательском режиме или программно, что дает полный контроль над выводом.
  2. Автоматическая обработка иерархии: СКД может автоматически генерировать дополнительные наборы данных и устанавливать связи для правильного отображения иерархии, даже для произвольных структур.
  3. Разделение данных и представления: Мы получаем данные запросом, а СКД заботится о том, как их представить, избегая дублирования.

2.2. Подход к работе с СКД для получения данных

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

Разберем по шагам этот подход:

  1. Формирование данных в СКД: Создайте схему компоновки данных, добавьте набор данных-запрос, настройте группировки (например, с типом "Иерархия" или "Только иерархия" для полей справочника).
  2. Выгрузка данных: После выполнения СКД вы можете выгрузить полученный результат в
    
    ДеревоЗначений
    
    или
    
    ТаблицуЗначений
    
    . Это позволит вам получить уже правильно структурированные иерархические данные без дублирования.
  3. Дальнейшая обработка: Полученное
    
    ДеревоЗначений
    
    или
    
    ТаблицаЗначений
    
    вы можете перебирать как угодно и выводить в ваш отчет в любом удобном формате.

Посмотрим на пример общего алгоритма:


// Старый подход (может приводить к дублированию при ручном обходе иерархии)
// тз = Запрос.Выполнить.Выгрузить();
// ВывестиСодержимоеТаблицыЗначенийВОтчет(тз);

// Новый, рекомендуемый подход с использованием СКД
СхемаКомпоновкиДанных = ПолучитьМакет("МояСхемаСКД"); // Или программно сформировать
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ТабличныйДокумент = Новый ТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);

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

// Далее работаем с РезультатСКД
ВывестиСодержимоеВОтчет(РезультатСКД);

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

Решение 3: Корректный обход результата запроса в коде

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


Выбрать()
без параметров может не дать ожидаемого результата при наличии итогов и группировок.

3.1. Использование

Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)

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


Выбрать()
с параметром

ОбходРезультатаЗапроса.ПоГруппировкамСИерархией
.

Разберем по шагам, как это работает:

  1. Начальный вызов: При первом вызове
    
    Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)
    
    возвращает только записи верхнего уровня иерархии.
  2. Рекурсивный обход: Для получения подчиненных записей (элементов внутри групп или групп внутри других групп) требуются последующие рекурсивные вызовы
    
    Выбрать()
    
    на полученной выборке.
  3. Указание поля группировки: При наличии нескольких уровней группировки может потребоваться указание имени поля группировки в качестве второго параметра метода
    
    Выбрать()
    
    (например,
    
    Выборка.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией, "МоеПолеГруппировки")
    
    ).

Посмотрим на пример кода для обхода иерархического результата запроса:


Функция ПолучитьДеревоИзЗапроса(Запрос)

    РезультатЗапроса = Запрос.Выполнить();
    Дерево = Новый ДеревоЗначений;
    // Добавляем колонки в Дерево по метаданным запроса
    Для Каждого Колонка Из РезультатЗапроса.Колонки Цикл
        Дерево.Колонки.Добавить(Колонка.Имя);
    КонецЦикла;

    Выборка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
    
    ОбходВыборки(Выборка, Дерево.Строки);

    Возврат Дерево;

КонецФункции

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

Используя этот подход, мы можем вручную построить


ДеревоЗначений
из иерархического результата запроса, избегая дублирования.

Особенности работы с консолями запросов

Важно отметить разницу между


Консолью запросов
и

Консолью отчетов
(или СКД).

Поэтому, если в


Консоли запросов
вы видите дублирование, а в

Консоли отчетов
или в отчете на СКД – нет, это не значит, что

Консоль запросов
"поломанная". Это лишь говорит о том, что эти инструменты имеют разное назначение и по-разному интерпретируют результат запроса.

Заключение

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


ТОЛЬКО ИЕРАРХИЯ
и

Поле.Родитель В ИЕРАРХИИ
, а также применение СКД для построения отчетов, являются наиболее надежными способами избежать этой проблемы. Если же вы работаете с запросом в коде, не забывайте про корректный обход результата с помощью

Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)
.

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

← К списку