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

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

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

Проблема объединения данных и расчет общих итогов

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

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

Решение 1: Использование технической колонки для итогов

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

Разберем по шагам реализацию этого метода. В первой части запроса, где мы выбираем остатки, создадим поле ИтогиОст. Во второй и третьей частях (резервы и продажи) в это же поле проставим 0. Посмотрим на пример кода:


ВЫБРАТЬ
    ТоварыНаСкладах.Склад КАК Склад,
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    СУММА(ТоварыНаСкладах.КоличествоОстаток) КАК Остаток,
    СУММА(ТоварыНаСкладах.КоличествоОстаток) КАК ИтогиОст
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК ТоварыНаСкладах
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Склад,
    ТоварыНаСкладах.Номенклатура

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    "Резерв",
    ТоварыВРезерве.Номенклатура,
    СУММА(ТоварыВРезерве.КоличествоОстаток),
    0 // Техническое поле для итога здесь равно нулю
ИЗ
    РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(&Дата) КАК ТоварыВРезерве
СГРУППИРОВАТЬ ПО
    ТоварыВРезерве.Номенклатура

ИТОГИ
    СУММА(Остаток),
    СУММА(ИтогиОст)
ПО
    Склад,
    Номенклатура

В данном случае колонка Остаток будет содержать детальные данные по всем веткам объединения, а колонка ИтогиОст в итоговых строках покажет сумму только из первой ветки (складские остатки). Чтобы «лишняя» колонка не мешала пользователю, её можно скрыть в настройках оформления построителя отчета или СКД, используя её только как расчетный ресурс.

Решение 2: Условный расчет в секции ИТОГИ

Посмотрим на альтернативный вариант — использование оператора ВЫБОР непосредственно внутри агрегатной функции в секции ИТОГИ. Этот метод позволяет не плодить лишние колонки в основной части запроса, но требует очень внимательного отношения к синтаксису.

Важный момент: часто программисты совершают ошибку, пытаясь написать условие внутри ИТОГИ без использования агрегатной функции. Например, конструкция ИТОГИ ВЫБОР КОГДА... приведет к синтаксической ошибке. Правильно использовать условие внутри СУММА().

Проанализируем ситуацию: нам нужно понять, относится ли текущая строка к первой таблице. Это можно сделать, проверив тип значения поля Склад или наличие специфического значения. Рассмотрим пример:


ИТОГИ
    СУММА(Остаток),
    СУММА(ВЫБОР
        КОГДА ТипЗначения(Склад) = ТИП(Справочник.Склады)
        ТОГДА Остаток
        ИНАЧЕ 0
    КОНЕЦ) КАК ИтогПоСкладу
ПО
    Склад,
    Номенклатура

Внимание: Такой подход будет работать только в том случае, если в других частях объединения поле Склад принимает значения другого типа (например, строку "Продажи" или "Резерв"). Если же везде используется один и тот же справочник, данный метод не подойдет, и лучше вернуться к Решению №1.

Решение 3: Архитектурный подход через временные таблицы и соединения

Если запрос становится слишком сложным, а итоги продолжают «двоиться», мы рекомендуем отказаться от оператора ОБЪЕДИНИТЬ в пользу временных таблиц и левого соединения (LEFT JOIN). Это наиболее профессиональный подход в 1С.

Рассмотрим алгоритм действий:

  1. Создаем временную таблицу ВТ_Остатки, куда выбираем данные из регистра остатков.
  2. Создаем временную таблицу ВТ_Резервы.
  3. Создаем временную таблицу ВТ_Продажи.
  4. В финальном запросе выбираем данные из ВТ_Остатки и левым соединением по Номенклатуре (и Складу) присоединяем остальные таблицы.

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


ВЫБРАТЬ
    Остатки.Склад КАК Склад,
    Остатки.Номенклатура КАК Номенклатура,
    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК ПолеОстаток,
    ЕСТЬNULL(Резервы.КоличествоОстаток, 0) КАК ПолеРезерв,
    ЕСТЬNULL(Продажи.КоличествоОборот, 0) КАК ПолеПродажи
ИЗ
    ВТ_Остатки КАК Остатки
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Резервы КАК Резервы ПО Остатки.Номенклатура = Резервы.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК Продажи ПО Остатки.Номенклатура = Продажи.Номенклатура
ИТОГИ
    СУММА(ПолеОстаток),
    СУММА(ПолеРезерв),
    СУММА(ПолеПродажи)
ПО
    Склад,
    Номенклатура

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

Полезные советы при работе с итогами

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

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

Подводя итог, можно сказать: наиболее простым и быстрым способом является добавление технической колонки с нулями во всех «ненужных» частях объединения. Однако для качественной архитектуры решения лучше использовать временные таблицы и соединения, что исключит саму возможность логической ошибки при расчете итогов.

← На главную