Как вывести данные из нескольких табличных частей в одно поле СКД без задвоения строк?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов в системе компоновки данных (СКД) программисты часто сталкиваются с задачей вывода информации из нескольких независимых табличных частей одного объекта. Классический пример — отчет по сотрудникам, в котором необходимо одновременно показать состав его семьи (ФИО родственников, степень родства) и полученное образование (виды образования, учебные заведения). У одного сотрудника может быть несколько родственников и несколько дипломов об образовании.

Если попытаться выбрать эти данные простым запросом, объединяющим две табличные части по ссылке на физическое лицо, СУБД выполнит декартово произведение (строки перемножатся). В результате, если у сотрудника три родственника и два образования, в отчете выведутся шесть строк, где данные будут дублироваться и хаотично перекрещиваться. Рассмотрим подробнее, как решить эту проблему и получить красивый, аккуратный отчет без дублирования записей.

Выясняем причину проблемы: почему происходит задвоение?

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

Для решения этой задачи в 1С существует несколько эффективных подходов. Мы разберем по шагам четыре основных метода: от использования встроенных функций СКД до перестройки архитектуры запроса.

---

Способ 1. Использование функций СКД группировки и свертки строк (Решение с форума)

Этот метод позволяет решить проблему непосредственно на уровне настроек схемы компоновки данных, не переписывая исходный запрос. Мы используем встроенные функции СКД ВычислитьВыражениеСГруппировкойТаблицаЗначений (или ее более легкий аналог ВычислитьВыражениеСГруппировкойМассив) совместно с функцией СоединитьСтроки.

Разберем алгоритм настройки этого механизма:

  1. Перейдем на вкладку «Вычисляемые поля» в конструкторе схемы компоновки данных.
  2. Добавим новое вычисляемое поле, например, СтепеньРодстваРодственника. Поле «Выражение» оставим пустым.
  3. Добавим еще одно вычисляемое поле для образования — ВидОбразованияСотрудника.
  4. Перейдем на вкладку «Ресурсы». Здесь происходит основное волшебство агрегации.
  5. Выберем поле СтепеньРодстваРодственника и в колонке «Выражение» пропишем формулу, которая соберет уникальные значения в массив и соединит их через разделитель (например, перенос строки):

СоединитьСтроки(ВычислитьВыражениеСГруппировкойМассив("ДанныеОФизЛице.СоставСемьи.ФизЛицоСтепеньРодства", "ДанныеОФизЛице.СоставСемьи.ФизЛицоСтепеньРодства"), , Символы.ПС)

В поле «Рассчитывать по...» обязательно укажем группировку верхнего уровня — Сотрудник (или ФизическоеЛицо).

Для поля образования настроим аналогичный ресурс:


СоединитьСтроки(ВычислитьВыражениеСГруппировкойМассив("ДанныеОФизЛице.Образование.ФизЛицоВидОбразования", "ДанныеОФизЛице.Образование.ФизЛицоВидОбразования"), , Символы.ПС)

Как это работает под капотом СКД: Функция ВычислитьВыражениеСГруппировкойМассив собирает все значения детальных записей по указанному пути, группирует их (убирая дубли за счет указания поля группировки во втором параметре) и возвращает массив уникальных значений для каждого сотрудника. Затем функция СоединитьСтроки преобразует этот массив в одну строку, разделенную символом переноса строки. Это позволяет вывести всю информацию компактно в одной ячейке без дублирования строк в отчете.

---

Способ 2. Связи наборов данных (Канонический метод СКД)

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

Проанализируем этот процесс по шагам:

  1. Создадим НаборДанных1 (Запрос): Выбирает основных сотрудников.
    
    ВЫБРАТЬ
        Сотрудники.Ссылка КАК Сотрудник,
        Сотрудники.ФизическоеЛицо КАК ФизическоеЛицо
    ИЗ
        Справочник.Сотрудники КАК Сотрудники
    
  2. Создадим НаборДанных2 (Запрос): Выбирает только данные по родственникам.
    
    ВЫБРАТЬ
        СоставСемьи.Ссылка КАК ФизическоеЛицо,
        СоставСемьи.СтепеньРодства КАК СтепеньРодства,
        СоставСемьи.ФизЛицоРодственник КАК ФизЛицоРодственник
    ИЗ
        Справочник.ФизическиеЛица.СоставСемьи КАК СоставСемьи
    
  3. Создадим НаборДанных3 (Запрос): Выбирает только данные по образованию.
    
    ВЫБРАТЬ
        Образование.Ссылка КАК ФизическоеЛицо,
        Образование.ВидОбразования КАК ВидОбразования
    ИЗ
        Справочник.ФизическиеЛица.Образование КАК Образование
    
  4. Перейдем на вкладку «Связи наборов данных» и настроим связи:
    • Связь 1: Источник — НаборДанных1, Приемник — НаборДанных2. Условие связи: ФизическоеЛицо = ФизическоеЛицо.
    • Связь 2: Источник — НаборДанных1, Приемник — НаборДанных3. Условие связи: ФизическоеЛицо = ФизическоеЛицо.

При такой настройке СКД выполнит три отдельных оптимизированных запроса к базе данных. Проблема декартова произведения исчезнет сама собой, так как таблицы не перемножаются на уровне СУБД. На вкладке «Ресурсы» мы сможем применить к полям подчиненных наборов данных стандартную агрегатную функцию СоединитьСтроки(Массив(СтепеньРодства)), получив чистый и быстро работающий отчет.

---

Способ 3. Полное соединение по номеру строки в запросе

Если необходимо вывести данные параллельно в виде таблицы (чтобы в первой строке сотрудника вывелись его первый родственник и его первое образование, во второй строке — второй родственник и второе образование), можно применить математический трюк сопоставления по номеру строки табличной части.

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    ЕСТЬNULL(ТаблицаОбразования.Ссылка, ТаблицаСемьи.Ссылка) КАК ФизическоеЛицо,
    ТаблицаОбразования.ВидОбразования КАК ВидОбразования,
    ТаблицаСемьи.СтепеньРодства КАК СтепеньРодства,
    ТаблицаСемьи.ФизЛицоРодственник КАК ФизЛицоРодственник
ИЗ
    Справочник.ФизическиеЛица.СоставСемьи КАК ТаблицаСемьи
    ПОЛНОЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица.Образование КАК ТаблицаОбразования
        ПО ТаблицаСемьи.Ссылка = ТаблицаОбразования.Ссылка
        И ТаблицаСемьи.НомерСтроки = ТаблицаОбразования.НомерСтроки

Важные нюансы этого метода:

---

Способ 4. Использование функций общего модуля (Программный метод)

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

Рассмотрим по шагам реализацию данного метода:

  1. Создадим общий модуль (например, КадровыеОтчетыСервер). Установим у модуля свойства: «Сервер», «Вызов сервера» и «Внешнее соединение».
  2. Напишем экспортную функцию, которая будет получать от СКД ссылку на физическое лицо, выполнять оптимальный запрос к табличной части и возвращать красиво отформатированную строку:

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

Преимущества метода: Максимальная гибкость. В коде 1С вы можете реализовать любые сортировки, условное оформление отдельных строк, склонение слов и сложную бизнес-логику.

Недостатки метода: Снижение производительности. Если в отчете выводятся тысячи сотрудников, вызов функции общего модуля из вычисляемого поля СКД приведет к выполнению запроса в цикле для каждой строки (проблема N+1 запроса). Используйте этот метод только на небольших объемах данных или кэшируйте результаты запросов.

---

Резюме: какой метод выбрать?

Выбор оптимального метода зависит от структуры вашего отчета и требований к производительности:

← На главную