При разработке отчетов и обработок, использующих деревья значений, разработчики часто сталкиваются с проблемой задвоения записей при выполнении левых соединений в запросе. Это приводит к тому, что агрегатные данные (остатки, суммы) суммируются неверно, а сортировка строк сбивается. Рассмотрим, как системно подойти к решению этой задачи и добиться корректного вывода данных.
Основная причина появления дублей кроется в архитектуре запроса. Когда мы присоединяем к основной таблице (например, к регистру Спецификация) другие таблицы, в которых по одному ключу (номенклатуре или регистратору) содержится несколько записей, система автоматически создает декартово произведение, дублируя строки основной выборки. Использование РАЗЛИЧНЫЕ часто «лечит» визуальную составляющую, но при этом нарушает логику нумерации строк и затрудняет дальнейшую обработку данных.
Наиболее надежный способ борьбы с дублями и одновременно — с проблемой сортировки, это использование пакетов запросов с временными таблицами. Мы разбиваем процесс на два этапа:
Рассмотрим пример реализации такой структуры:
ВЫБРАТЬ
Спецификация.Регистратор,
Спецификация.Номенклатура,
Спецификация.Проект,
Спецификация.ЗаявкаНаКомплектацию КАК ДокументЗаявки,
МИНИМУМ(Спецификация.НомерСтроки) КАК НомерСтроки
ПОМЕСТИТЬ тСпецификация
ИЗ РегистрНакопления.Спецификация КАК Спецификация
СГРУППИРОВАТЬ ПО
Спецификация.Регистратор,
Спецификация.Номенклатура,
Спецификация.Проект,
Спецификация.ЗаявкаНаКомплектацию;
После того как данные помещены во временную таблицу тСпецификация, мы можем смело присоединять остатки и другие данные, не опасаясь, что количество строк увеличится сверх меры.
При использовании виртуальных таблиц, таких как Спецификация.Остатки, важно ограничивать выборку. Обращение к виртуальной таблице без фильтрации по номенклатуре приводит к перебору всего регистра, что замедляет работу системы. Мы можем передать список номенклатур из нашей временной таблицы прямо в условия виртуальной таблицы:
ВЫБРАТЬ
тСпец.Регистратор,
СУММА(ЕСТЬNULL(СпецОстатки.КоличествоОстаток, 0)) КАК ОстатокЗаявки
ИЗ тСпецификация КАК тСпец
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Спецификация.Остатки(
,
Номенклатура В (ВЫБРАТЬ т.Номенклатура ИЗ тСпецификация КАК т)
) КАК СпецОстатки
ПО тСпец.Номенклатура = СпецОстатки.Номенклатура
И тСпец.Регистратор = СпецОстатки.ЗаявкаНаКомплектацию
СГРУППИРОВАТЬ ПО тСпец.Регистратор
Чтобы дерево значений в точности повторяло порядок строк в исходном документе, необходимо:
НомерСтроки в группировку начального запроса.МИНИМУМ(НомерСтроки) или МАКСИМУМ(НомерСтроки), чтобы обеспечить уникальность ключа.УПОРЯДОЧИТЬ ПО Регистратор, НомерСтроки.При выводе результата в дерево, используйте метод Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам). Это позволит платформе автоматически распределить записи по иерархическим уровням, сохраняя заданную нами сортировку внутри каждого уровня.
Использование Запрос.Выполнить() внутри цикла обхода дерева — это критическая ошибка проектирования. Каждый вызов запроса к базе данных создает нагрузку на сервер. Вместо этого:
ТаблицаЗначений.НайтиСтроки() или Найти() по коллекции строк в памяти.Если вы следуете этим рекомендациям, вы сможете избавиться от «танцев с бубном» при группировке данных и обеспечите стабильную, быструю работу вашего отчета, при этом полностью сохранив читаемость структуры документа в дереве значений.