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

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда необходимо сопоставить данные из разных регистров или таблиц в одном отчете, ориентируясь на общее ключевое поле — чаще всего это Дата или Период (например, в разрезе часа или дня). Типичный пример: нужно вывести в одной строке вес заказов на сборке и вес заказов на упаковке за каждый час, при этом данные о сборке и упаковке могут храниться в разных записях одного регистра или вовсе в разных таблицах.

Рассмотрим ситуацию, когда простое соединение таблиц не дает нужного результата из-за отсутствия данных в одном из периодов или из-за дублирования строк. Мы разберем наиболее надежный и производительный метод решения этой задачи — использование конструкции ОБЪЕДИНИТЬ ВСЕ с последующей группировкой данных.

Метод «Объединение + Группировка»: основной алгоритм

Многие начинающие разработчики пытаются использовать ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ. Однако, если у вас более двух источников данных, дерево соединений становится громоздким и трудночитаемым. Метод объединения гораздо проще масштабируется: сколько бы таблиц-источников у вас ни было, вы просто добавляете новый блок в запрос.

Основная идея заключается в том, чтобы в каждой части объединения создать одинаковый набор колонок, но заполнять только ту колонку, которая относится к текущему источнику. В остальных колонках мы проставляем пустые значения (обычно это 0 для чисел).

Разберем по шагам создание такого запроса на примере задачи автора темы:

  1. Создаем первую часть запроса: выбираем данные по сборке. Создаем колонку ВесСборка, а для будущей колонки упаковки ставим 0.
  2. Создаем вторую часть запроса: выбираем данные по упаковке. Для колонки ВесСборка ставим 0, а колонку ВесУпаковка заполняем реальными данными.
  3. Объединяем: используем оператор ОБЪЕДИНИТЬ ВСЕ.
  4. Группируем: помещаем результат объединения во вложенный запрос или временную таблицу и выполняем группировку по полю Час, суммируя числовые показатели.

Проанализируем правильную структуру кода, которая позволяет «схлопнуть» данные в одну строку:


ВЫБРАТЬ
    ВложенныйЗапрос.Час КАК Час,
    СУММА(ВложенныйЗапрос.ВесСборка) КАК ВесСборка,
    СУММА(ВложенныйЗапрос.ВесУпаковка) КАК ВесУпаковка
ИЗ
    (ВЫБРАТЬ
        НАЧАЛОПЕРИОДА(Рег.ВремяСборки, ЧАС) КАК Час,
        Рег.Вес КАК ВесСборка,
        0 КАК ВесУпаковка
    ИЗ
        РегистрСведений.vl_МониторОбработкиЗаказов КАК Рег
    ГДЕ
        Рег.ВремяСборки МЕЖДУ &НачалоПериода И &КонецПериода

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

    ВЫБРАТЬ
        НАЧАЛОПЕРИОДА(Рег.ВремяУпаковки, ЧАС),
        0,
        Рег.Вес
    ИЗ
        РегистрСведений.vl_МониторОбработкиЗаказов КАК Рег
    ГДЕ
        Рег.ВремяУпаковки МЕЖДУ &НачалоПериода И &КонецПериода) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.Час

Почему важно использовать «0» вместо «NULL»?

В языке запросов 1С при объединении таблиц типы колонок должны совпадать. Если в первой таблице колонка Вес имеет числовой тип, то и во второй таблице в этой же позиции должно быть число. Хотя NULL технически может быть приведен к любому типу, использование 0 для агрегируемых ресурсов (сумм, веса, количества) предпочтительнее по нескольким причинам:

Альтернатива: Использование Полного соединения

Если таблиц всего две, можно рассмотреть ПОЛНОЕ СОЕДИНЕНИЕ. Это позволит избежать вложенного запроса, но потребует внимательности при работе с ключевым полем. Поскольку запись может существовать только в первой таблице или только во второй, выбор поля Час нужно делать через ЕСТЬNULL.

Рассмотрим пример такого подхода:


ВЫБРАТЬ
    ЕСТЬNULL(Т1.Час, Т2.Час) КАК Час,
    ЕСТЬNULL(Т1.ВесСборка, 0) КАК ВесСборка,
    ЕСТЬNULL(Т2.ВесУпаковка, 0) КАК ВесУпаковка
ИЗ
    тзЗаказыНаСборке КАК Т1
        ПОЛНОЕ СОЕДИНЕНИЕ тзЗаказыНаУпаковке КАК Т2
        ПО Т1.Час = Т2.Час

Важный нюанс: метод полного соединения плохо работает, если в ключевом поле (в нашем случае Час) есть дубли. Если за один и тот же час в первой таблице будет 2 строки, а во второй 3, соединение породит 6 строк (произведение), что исказит итоговые суммы. Именно поэтому метод Объединить все + Группировка считается более безопасным и универсальным.

Решение проблемы «дыр» в периодах

Бывает так, что в итоговом отчете нужно вывести все часы суток, даже те, в которых не было ни сборки, ни упаковки. Ни ОБЪЕДИНИТЬ, ни ПОЛНОЕ СОЕДИНЕНИЕ сами по себе не создадут строк, которых нет в базе.

Выясним, как решить эту задачу. Для этого в пакетном запросе сначала формируют «эталонную» таблицу со всеми часами (например, с помощью производственного календаря или временной таблицы, заполненной программно). Затем к этой таблице применяют ЛЕВОЕ СОЕДИНЕНИЕ со всеми нашими данными.

Рассмотрим структуру такого сложного запроса:

  1. ВТ_Календарь: содержит колонку Час (все 24 значения за сутки).
  2. ВТ_Данные: результат объединения сборки и упаковки (как мы разобрали выше).
  3. Итоговый запрос: ВТ_Календарь ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные ПО Час.

Использование СКД для объединения

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

Подводя итог, можно сказать: для простых запросов в коде используйте ОБЪЕДИНИТЬ ВСЕ с нулевыми колонками-заглушками. Для сложных отчетов с иерархией и множеством условий лучше доверить объединение механизмов СКД.

← На главную