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