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

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

При работе с запросами в системе 1С:Предприятие (поможет универсальная консоль запросов и инструменты разработчика) программисты часто сталкиваются с ситуацией, когда данные из разных источников (например, разные регистры или временные таблицы) необходимо вывести в одной строке. Стандартное использование оператора ОБЪЕДИНИТЬ (UNION) часто приводит к тому, что вместо одной компактной строки мы получаем несколько: в одной заполнены одни колонки, а в другой — остальные. Сегодня мы подробно разберем, почему так происходит, и научимся правильно «схлопывать» эти данные в одну итоговую запись.

Анализ проблемы: почему ОБЪЕДИНИТЬ выдает две строки вместо одной

Рассмотрим типичную ситуацию, с которой столкнулся автор темы. У нас есть два источника данных, которые имеют общие ключевые поля (например, ЗаказКл, Номенклатура, Договор) и специфические числовые показатели (выручка в одном запросе, трудозатраты в другом). Проблема заключается в том, что оператор ОБЪЕДИНИТЬ соединяет наборы записей вертикально. Если в первой части объединения поле «Трудозатраты» отсутствует (заполнено как NULL или 0), а во второй части заполнено значением, то для системы это разные записи.

Проанализируем пример кода из вопроса. Программист применил СГРУППИРОВАТЬ ПО внутри каждой части объединения. Это правильный шаг для очистки данных внутри подзапросов, но это никак не влияет на результат самого объединения. Платформа 1С сначала выполняет первый запрос, затем второй, а потом просто «склеивает» их результаты. Если записи не идентичны во всех полях, они останутся разными строками.

Метод 1: Использование вложенного запроса с внешней группировкой

Самый надежный и распространенный способ решить задачу — обернуть объединение во внешний запрос и выполнить итоговую группировку. Разберем этот процесс по шагам:

  1. Создадим вложенный запрос (подзапрос), внутри которого перечислим все необходимые поля.
  2. Во всех ветках объединения обеспечим одинаковый состав полей. Там, где данных для конкретной колонки нет, подставим 0 (для чисел) или ЗНАЧЕНИЕ(...) (для ссылок).
  3. Во внешнем запросе выберем эти поля, применив к числовым показателям агрегатную функцию СУММА.
  4. Сгруппируем результат по всем нечисловым полям (измерениям).

Рассмотрим, как должен выглядеть правильный шаблон запроса:


ВЫБРАТЬ
    ВложенныйЗапрос.ЗаказКл КАК ЗаказКл,
    ВложенныйЗапрос.Номенклатура КАК Номенклатура,
    СУММА(ВложенныйЗапрос.АктВыручка) КАК АктВыручка,
    СУММА(ВложенныйЗапрос.АктЗатраты) КАК АктЗатраты,
    СУММА(ВложенныйЗапрос.АктКоличество) КАК АктКоличество,
    СУММА(ВложенныйЗапрос.Трудозатраты) КАК Трудозатраты
ИЗ
    (ВЫБРАТЬ
        ВТ1.ЗаказКл КАК ЗаказКл,
        ВТ1.Номенклатура КАК Номенклатура,
        ВТ1.Выручка КАК АктВыручка,
        ВТ1.Затраты КАК АктЗатраты,
        ВТ1.Выпуск КАК АктКоличество,
        0 КАК Трудозатраты
    ИЗ
        ВТ1 КАК ВТ1
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ВТ23.ЗаказКл,
        ВТ23.Номенклатура,
        0,
        0,
        0,
        ВТ23.Трудозатраты
    ИЗ
        ВТ23 КАК ВТ23) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.ЗаказКл,
    ВложенныйЗапрос.Номенклатура

Обратите внимание: мы используем ОБЪЕДИНИТЬ ВСЕ. Это важный момент для производительности. Обычный ОБЪЕДИНИТЬ заставляет платформу искать дубликаты строк и удалять их, что требует дополнительных ресурсов. Поскольку мы всё равно выполняем СГРУППИРОВАТЬ ПО во внешнем запросе, поиск дублей на этапе объединения избыточен.

Метод 2: Использование временных таблиц для сложной логики

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

Выясним причину, почему это удобнее: временные таблицы позволяют отлаживать каждую часть запроса отдельно и снижают нагрузку на оптимизатор базы данных при очень сложных связях.

Рассмотрим алгоритм работы с временными таблицами:

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

Пример реализации через временные таблицы:


// Первый пакет - заполняем выручку
ВЫБРАТЬ
    ВТ1.ЗаказКл,
    ВТ1.Выручка КАК Сумма,
    0 КАК Трудозатраты
ПОМЕСТИТЬ ВТ_Объединение
ИЗ
    ВТ1 КАК ВТ1

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

// Добавляем данные по трудозатратам в ту же таблицу
ВЫБРАТЬ
    ВТ2.ЗаказКл,
    0,
    ВТ2.Трудозатраты
ИЗ
    ВТ2 КАК ВТ2
;

// Финальный пакет - схлопываем строки
ВЫБРАТЬ
    ВТ_Объединение.ЗаказКл,
    СУММА(ВТ_Объединение.Сумма) КАК Сумма,
    СУММА(ВТ_Объединение.Трудозатраты) КАК Трудозатраты
ИЗ
    ВТ_Объединение КАК ВТ_Объединение
СГРУППИРОВАТЬ ПО
    ВТ_Объединение.ЗаказКл

Важные технические нюансы и оптимизация

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

1. Обработка NULL. При использовании ЛЕВОЕ СОЕДИНЕНИЕ, если запись в правой таблице не найдена, поля примут значение NULL. Суммирование NULL с числом даст NULL в некоторых СУБД (хотя 1С старается это нивелировать). Профессиональным подходом считается использование функции ЕСТЬNULL(Поле, 0). Это гарантирует, что в объединение попадут чистые числа, готовые к математическим операциям.

2. Группировка нечисловых данных. Если вам нужно вытянуть в итоговую строку реквизит, который не является числом (например, Договор или Комментарий), и он заполнен только в одной из веток объединения, используйте агрегатную функцию МАКСИМУМ. Это позволит «подтянуть» значение во внешнюю строку, не включая это поле в секцию СГРУППИРОВАТЬ ПО, что предотвратит лишнее дробление строк.

3. Соответствие типов. Платформа 1С требует, чтобы колонки во всех частях объединения имели совместимые типы. Если в первой ветке третья колонка — это Число, а во второй ветке вы случайно поставите туда Строку или Ссылку, запрос выдаст ошибку. Всегда проверяйте порядок полей в секции ВЫБРАТЬ.

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

← На главную