Как правильно соединить множество временных таблиц по номенклатуре в запросе и СКД

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

При разработке комплексных аналитических отчетов в 1С часто возникает задача собрать в единую широкую таблицу множество показателей: остатки, продажи, закупки, списания, возвраты, цены и производные коэффициенты (оборачиваемость, доли и т.д.). Когда разработчик пытается связать 10 и более временных таблиц через последовательные соединения, возникают типовые проблемы: дублирование данных (декартово произведение), обнуление строк из-за пустых выборок, путаница с ЕСТЬNULL или просадка производительности на стороне СУБД.

Разберем основные причины таких ошибок и рассмотрим правильные архитектурные подходы для объединения разнородных данных в запросах 1С и схеме компоновки данных (СКД).

Анализ типовых ошибок при каскадных соединениях

Рассмотрим, почему прямой подход с большим количеством ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ часто приводит к сбоям:

  1. Неявное декартово произведение (Cross Join): Если во временной таблице перечисляются источники через запятую без условий связи (например, выборка оборотов номенклатуры и общей суммы продаж без условия соединения), при отсутствии данных во второй таблице обнулится весь результат выборки.
  2. Потеря ключей при полном соединении: Конструкция вида ЕСТЬNULL(ЕСТЬNULL(Таблица1.Номенклатура, Таблица2.Номенклатура), Таблица3.Номенклатура) для десятка таблиц становится громоздкой, тяжело читаемой и крайне медленно исполняется СУБД.
  3. Неоднозначность связи (1 ко многим): Если хотя бы в одной временной таблице данные не сгруппированы строго по ключевому полю (например, номенклатура дублируется в разрезе складов или документов), левое соединение размножит строки во всех остальных показателях.
  4. Неоптимальные планы запросов: Большое число левых соединений временных таблиц без индексов перегружает оптимизатор СУБД, вызывая медленные операции вложенных циклов.

Вариант 1. Паттерн «ОБЪЕДИНИТЬ ВСЕ» со сверткой (рекомендуемый для запросов)

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

Посмотрим на пример правильной структуры запроса:


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

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

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

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

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

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

    ВЫБРАТЬ
        Цены.Номенклатура,
        0,
        0,
        0,
        0,
        Цены.Цена
    ИЗ
        РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ПериодОстатков, ТипЦен = &ТипЦены) КАК Цены) КАК Вложенный

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

Преимущества подхода:

  1. Номенклатура всегда выводится корректно, независимо от того, в каком из регистров были движения.
  2. Исключается декартово произведение строк.
  3. СУБД выполняет быструю потоковую агрегацию вместо тяжелых соединений.

Вариант 2. Базовый справочник и фильтрация пустых строк

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

Разберем пример запроса:


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

Условие в секции ГДЕ исключает элементы справочника, по которым нет показателей ни в одной из соединенных временных таблиц.

Вариант 3. Использование связей наборов данных в СКД

Когда отчет строится на СКД, объединение таблиц можно вынести из тела запроса на уровень платформы 1С:

  1. Создаем несколько независимых наборов данных — Запрос (один набор для продаж, второй для остатков, третий для цен и т.д.).
  2. На вкладке «Связи наборов данных» связываем наборы по полю Номенклатура.
  3. Для скрытия строк без движений устанавливаем отбор на уровне настроек отчета: на вкладке «Отбор» добавляем условия по ресурсам (например, СуммаПродажи Не равно 0 ИЛИ Остаток Не равно 0). СКД применяет такие отборы после агрегации, как условие HAVING.

Особенности расчета итогов и долей в СКД

При расчете итоговых показателей (например, процента от общей выручки или суммы минимумов) избегайте лишних временных таблиц в запросе — используйте функции СКД на вкладке «Ресурсы»:

  1. Расчет доли от общего итога: выражение Сумма(СуммаПродажи) / ВычислитьВыражение("Сумма(СуммаПродажи)", "") * 100 или функция ПроцентВ(Сумма(СуммаПродажи), "ОбщийИтог").
  2. Сумма значений группировок (сумма минимумов/максимумов): выражение Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(Количество)", "Номенклатура")).
← На главную