При построении запросов к нескольким табличным частям одного документа (например, к табличным частям «Работы» и «Запчасти» в документе заказ-наряда) разработчики часто сталкиваются с ситуацией, когда в итоговой выборке строки начинает «раздувать». Вместо компактного списка данных запрос выводит каждую работу с каждой запчастью, образуя так называемое декартово произведение.
В этой статье мы разберем причину возникновения дублей, проанализируем различные способы решения этой задачи в платформе 1С и рассмотрим подробнее реализации с помощью горизонтального соединения и вертикального объединения выборок.
Проблема возникает из-за некорректного использования ключевого поля при соединении табличных частей. Если соединять табличные части Работы и Запчасти исключительно по условию ПОЛНОЕ СОЕДИНЕНИЕ ... ПО ТЧ1.Ссылка = ТЧ2.Ссылка, СУБД сопоставляет каждую строку из первой табличной части со всеми строками из второй табличной части в пределах одного документа.
Если в документе добавлено 3 работы и 4 запчасти, результат такого соединения содержит 3 × 4 = 12 строк. Попытка применить ключевое слово РАЗЛИЧНЫЕ в последнем запросе не решает проблему, так как каждая строка уникальна по сочетанию номенклатуры работ и запчастей.
Кроме разрастания строк, декартово произведение несет скрытую угрозу: при расчете итоговых сумм через СУММА() значения будуткратно завышены, что приводит к искажению финансовых результатов в отчетах.
Если требуется вывести данные горизонтально в одной строке (в одной колонке — работа, в соседней — запчасть) и строки ТЧ не имеют жесткой логической связи между собой, можно связать их по дополнительному ключу — полю НомерСтроки.
Разберем по шагам, как это реализовать на практике:
НомерСтроки для каждой табличной части.ПОЛНОЕ СОЕДИНЕНИЕ по двум полям: Ссылка и НомерСтроки.ЕСТЬNULL() для обработки ситуаций, когда количество строк в одной табличной части превышает количество строк в другой.Рассмотрим пример кода 1С:
ВЫБРАТЬ
асЗаказНарядРаботы.Ссылка КАК Ссылка,
асЗаказНарядРаботы.НомерСтроки КАК НомерСтроки,
асЗаказНарядРаботы.Работа КАК Работа,
асЗаказНарядРаботы.Сумма КАК СуммаРабот
ПОМЕСТИТЬ т_Работы
ИЗ
Документ.асЗаказНаряд.Работы КАК асЗаказНарядРаботы
ГДЕ
асЗаказНарядРаботы.Ссылка = &Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
асЗаказНарядЗапчасти.Ссылка КАК Ссылка,
асЗаказНарядЗапчасти.НомерСтроки КАК НомерСтроки,
асЗаказНарядЗапчасти.Запчасть КАК Запчасть,
асЗаказНарядЗапчасти.Сумма КАК СуммаЗапчастей
ПОМЕСТИТЬ т_Запчасти
ИЗ
Документ.асЗаказНаряд.Запчасти КАК асЗаказНарядЗапчасти
ГДЕ
асЗаказНарядЗапчасти.Ссылка = &Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ЕСТЬNULL(т_Работы.Ссылка, т_Запчасти.Ссылка) КАК Ссылка,
т_Работы.Работа КАК Работа,
т_Работы.СуммаРабот КАК СуммаРабот,
т_Запчасти.Запчасть КАК Запчасть,
т_Запчасти.СуммаЗапчастей КАК СуммаЗапчастей
ИЗ
т_Работы КАК т_Работы
ПОЛНОЕ СОЕДИНЕНИЕ т_Запчасти КАК т_Запчасти
ПО т_Работы.Ссылка = т_Запчасти.Ссылка
И т_Работы.НомерСтроки = т_Запчасти.НомерСтроки
Важное ограничение: данный подход связывает строки исключительно по их физическому порядку в документе. Первая работа соединится с первой запчастью, вторая — со второй. Смысловой связи между ними нет, а при больших объемах данных такое соединение может уступать в производительности из-за отсутствия индексов по полю НомерСтроки.
В большинстве задач учета работы и запчасти представляют собой несвязанные перечни номенклатуры. Наиболее корректным архитектурным решением в языке запросов 1С является сведение таблиц через объединение выборок, а не через соединение.
При использовании ОБЪЕДИНИТЬ ВСЕ мы последовательно выбираем данные из обеих табличных частей, подставляя пустые значения (или ЗНАЧЕНИЕ(...) / NULL) в отсутствующие колонки.
Проанализируем пример такого запроса:
ВЫБРАТЬ
ТЧРаботы.Ссылка КАК Ссылка,
ТЧРаботы.Работа КАК Номенклатура,
"Работа" КАК ТипЗаписи,
ТЧРаботы.Сумма КАК Сумма
ИЗ
Документ.асЗаказНаряд.Работы КАК ТЧРаботы
ГДЕ
ТЧРаботы.Ссылка = &Ссылка
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТЧЗапчасти.Ссылка КАК Ссылка,
ТЧЗапчасти.Запчасть КАК Номенклатура,
"Запчасть" КАК ТипЗаписи,
ТЧЗапчасти.Сумма КАК Сумма
ИЗ
Документ.асЗаказНаряд.Запчасти КАК ТЧЗапчасти
ГДЕ
ТЧЗапчасти.Ссылка = &Ссылка
Плюсы этого метода:
Если задача заключается в построении отчета в Системе компоновки данных (СКД), лучшим решением будет отказаться от объединения или соединения табличных частей внутри одного запроса.
Разберем алгоритм настройки:
При таком подходе платформа 1С берет на себя вывод данных, исключая задвоение записей и обеспечивая высокую гибкость при проектировании печатных форм и отчетов.