Почему в запросах 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С. Успехов в работе!

← На главную