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

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

При построении запросов к нескольким табличным частям одного документа (например, к табличным частям «Работы» и «Запчасти» в документе заказ-наряда) разработчики часто сталкиваются с ситуацией, когда в итоговой выборке строки начинает «раздувать». Вместо компактного списка данных запрос выводит каждую работу с каждой запчастью, образуя так называемое декартово произведение.

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

Выясним причину размножения строк

Проблема возникает из-за некорректного использования ключевого поля при соединении табличных частей. Если соединять табличные части Работы и Запчасти исключительно по условию ПОЛНОЕ СОЕДИНЕНИЕ ... ПО ТЧ1.Ссылка = ТЧ2.Ссылка, СУБД сопоставляет каждую строку из первой табличной части со всеми строками из второй табличной части в пределах одного документа.

Если в документе добавлено 3 работы и 4 запчасти, результат такого соединения содержит 3 × 4 = 12 строк. Попытка применить ключевое слово РАЗЛИЧНЫЕ в последнем запросе не решает проблему, так как каждая строка уникальна по сочетанию номенклатуры работ и запчастей.

Кроме разрастания строк, декартово произведение несет скрытую угрозу: при расчете итоговых сумм через СУММА() значения будуткратно завышены, что приводит к искажению финансовых результатов в отчетах.

Вариант 1. Соединение табличных частей по номеру строки

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

Разберем по шагам, как это реализовать на практике:

  1. В помещениях временных таблиц выбираем поле НомерСтроки для каждой табличной части.
  2. В финальном запросе делаем ПОЛНОЕ СОЕДИНЕНИЕ по двум полям: Ссылка и НомерСтроки.
  3. Используем функцию ЕСТЬNULL() для обработки ситуаций, когда количество строк в одной табличной части превышает количество строк в другой.

Рассмотрим пример кода 1С:


ВЫБРАТЬ
    асЗаказНарядРаботы.Ссылка КАК Ссылка,
    асЗаказНарядРаботы.НомерСтроки КАК НомерСтроки,
    асЗаказНарядРаботы.Работа КАК Работа,
    асЗаказНарядРаботы.Сумма КАК СуммаРабот
ПОМЕСТИТЬ т_Работы
ИЗ
    Документ.асЗаказНаряд.Работы КАК асЗаказНарядРаботы
ГДЕ
    асЗаказНарядРаботы.Ссылка = &Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    асЗаказНарядЗапчасти.Ссылка КАК Ссылка,
    асЗаказНарядЗапчасти.НомерСтроки КАК НомерСтроки,
    асЗаказНарядЗапчасти.Запчасть КАК Запчасть,
    асЗаказНарядЗапчасти.Сумма КАК СуммаЗапчастей
ПОМЕСТИТЬ т_Запчасти
ИЗ
    Документ.асЗаказНаряд.Запчасти КАК асЗаказНарядЗапчасти
ГДЕ
    асЗаказНарядЗапчасти.Ссылка = &Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЕСТЬNULL(т_Работы.Ссылка, т_Запчасти.Ссылка) КАК Ссылка,
    т_Работы.Работа КАК Работа,
    т_Работы.СуммаРабот КАК СуммаРабот,
    т_Запчасти.Запчасть КАК Запчасть,
    т_Запчасти.СуммаЗапчастей КАК СуммаЗапчастей
ИЗ
    т_Работы КАК т_Работы
        ПОЛНОЕ СОЕДИНЕНИЕ т_Запчасти КАК т_Запчасти
        ПО т_Работы.Ссылка = т_Запчасти.Ссылка
        И т_Работы.НомерСтроки = т_Запчасти.НомерСтроки

Важное ограничение: данный подход связывает строки исключительно по их физическому порядку в документе. Первая работа соединится с первой запчастью, вторая — со второй. Смысловой связи между ними нет, а при больших объемах данных такое соединение может уступать в производительности из-за отсутствия индексов по полю НомерСтроки.

Вариант 2. Вертикальное объединение с помощью конструкции ОБЪЕДИНИТЬ ВСЕ

В большинстве задач учета работы и запчасти представляют собой несвязанные перечни номенклатуры. Наиболее корректным архитектурным решением в языке запросов 1С является сведение таблиц через объединение выборок, а не через соединение.

При использовании ОБЪЕДИНИТЬ ВСЕ мы последовательно выбираем данные из обеих табличных частей, подставляя пустые значения (или ЗНАЧЕНИЕ(...) / NULL) в отсутствующие колонки.

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


ВЫБРАТЬ
    ТЧРаботы.Ссылка КАК Ссылка,
    ТЧРаботы.Работа КАК Номенклатура,
    "Работа" КАК ТипЗаписи,
    ТЧРаботы.Сумма КАК Сумма
ИЗ
    Документ.асЗаказНаряд.Работы КАК ТЧРаботы
ГДЕ
    ТЧРаботы.Ссылка = &Ссылка

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ТЧЗапчасти.Ссылка КАК Ссылка,
    ТЧЗапчасти.Запчасть КАК Номенклатура,
    "Запчасть" КАК ТипЗаписи,
    ТЧЗапчасти.Сумма КАК Сумма
ИЗ
    Документ.асЗаказНаряд.Запчасти КАК ТЧЗапчасти
ГДЕ
    ТЧЗапчасти.Ссылка = &Ссылка

Плюсы этого метода:

Вариант 3. Использование нескольких наборов данных в СКД

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

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

  1. В схеме компоновки данных создаем Набор данных - запрос 1 для получения списка работ из первой табличной части.
  2. Создаем Набор данных - запрос 2 для получения запчастей из второй табличной части.
  3. В настройках структуры отчета добавляем две параллельные группировки или отдельную таблицу под каждый набор данных.

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

← На главную