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

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

При разработке отчетов по продажам (например, план-фактного анализа, отчетов по валовой прибыли или оценке рентабельности) программисты часто сталкиваются с необходимостью сопоставить данные по нескольким бухгалтерским счетам. Классический пример — получение в одной строке по каждой номенклатурной позиции (или номенклатурной группе) сразу трех показателей: Выручки (кредитовый оборот счета 90.01), Себестоимости (дебетовый оборот счета 90.02) и НДС (дебетовый оборот счета 90.03).

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

Выясняем причины: почему соединения (JOIN) работают неэффективно?

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

  1. Избыточность таблицы ОборотыДтКт. Данная таблица предназначена исключительно для анализа корреспонденции между дебетом и кредитом (когда важно знать, с какого именно счета на какой ушла сумма). Если нам нужно просто получить обороты по конкретным счетам независимо от их корреспондирующих счетов, использование ОборотыДтКт создает колоссальную и неоправданную нагрузку на СУБД.
  2. Проблема ПОЛНОГО СОЕДИНЕНИЯ. При полном соединении по субконто, если по какому-то товару была выручка, но не было себестоимости (или наоборот), СУБД вернет пустые значения (NULL). Кроме того, полные соединения очень ресурсоемки и при больших объемах данных могут приводить к зависанию отчета или перегрузке памяти сервера.
  3. Разъезжание данных по строкам. Если у разных временных таблиц не совпадут ключи связи или в одной из них будет несколько записей по одному субконто, данные начнут дублироваться или выводиться в разных строках, что сделает невозможным расчет итоговых показателей (например, прибыли как разности выручки и себестоимости).

Рассмотрим правильные и оптимизированные варианты решения этой задачи.

---

Метод 1. Условное агрегирование в одном запросе (Рекомендуемый метод)

Самый быстрый и производительный способ получить данные по нескольким счетам в одну строку — использовать виртуальную таблицу РегистрБухгалтерии.Хозрасчетный.Обороты и распределить суммы по колонкам с помощью конструкции ВЫБОР КОГДА внутри агрегатной функции СУММА. Этот прием позволяет СУБД прочитать данные из базы за один проход, без создания временных таблиц и соединений.

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


ВЫБРАТЬ
    ХозрасчетныйОбороты.Субконто1 КАК Номенклатура,
    СУММА(ВЫБОР
            КОГДА ХозрасчетныйОбороты.Счет = &Счет90_01
                ТОГДА ХозрасчетныйОбороты.СуммаОборотКт
            ИНАЧЕ 0
        КОНЕЦ) КАК Выручка,
    СУММА(ВЫБОР
            КОГДА ХозрасчетныйОбороты.Счет = &Счет90_02
                ТОГДА ХозрасчетныйОбороты.СуммаОборотДт
            ИНАЧЕ 0
        КОНЕЦ) КАК Себестоимость,
    СУММА(ВЫБОР
            КОГДА ХозрасчетныйОбороты.Счет = &Счет90_03
                ТОГДА ХозрасчетныйОбороты.СуммаОборотДт
            ИНАЧЕ 0
        КОНЕЦ) КАК НДС,
    СУММА(ВЫБОР
            КОГДА ХозрасчетныйОбороты.Счет = &Счет90_01
                ТОГДА ХозрасчетныйОбороты.СуммаОборотКт
            ИНАЧЕ 0
        КОНЕЦ) - СУММА(ВЫБОР
            КОГДА ХозрасчетныйОбороты.Счет = &Счет90_03
                ТОГДА ХозрасчетныйОбороты.СуммаОборотДт
            ИНАЧЕ 0
        КОНЕЦ) КАК СуммаБезНДС
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(
            &НачалоПериода,
            &КонецПериода,
            Период,
            Счет В (&СписокСчетов90),
            ,
            Организация = &Организация,
            ,
            
        ) КАК ХозрасчетныйОбороты

СГРУППИРОВАТЬ ПО
    ХозрасчетныйОбороты.Субконто1

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

---

Метод 2. Объединение (UNION) с последующим «схлопыванием»

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

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

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

Посмотрим на пример реализации этой схемы в коде запроса:


// Шаг 1. Получаем данные по первому счету (Выручка)
ВЫБРАТЬ
    ХозрасчетныйОбороты.Субконто1 КАК Номенклатура,
    ХозрасчетныйОбороты.СуммаОборотКт КАК Выручка,
    0 КАК Себестоимость,
    0 КАК НДС
ПОМЕСТИТЬ ВТ_ВсеДанные
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(&НачалоПериода, &КонецПериода, , Счет = &Счет90_01, , Организация = &Организация, , ) КАК ХозрасчетныйОбороты

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

// Шаг 2. Получаем данные по второму счету (Себестоимость)
ВЫБРАТЬ
    ХозрасчетныйОбороты.Субконто1,
    0,
    ХозрасчетныйОбороты.СуммаОборотДт,
    0
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(&НачалоПериода, &КонецПериода, , Счет = &Счет90_02, , Организация = &Организация, , ) КАК ХозрасчетныйОбороты

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

// Шаг 3. Получаем данные по третьему счету (НДС)
ВЫБРАТЬ
    ХозрасчетныйОбороты.Субконто1,
    0,
    0,
    ХозрасчетныйОбороты.СуммаОборотДт
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(&НачалоПериода, &КонецПериода, , Счет = &Счет90_03, , Организация = &Организация, , ) КАК ХозрасчетныйОбороты
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 4. Схлопываем строки по Номенклатуре в итоговой выборке
ВЫБРАТЬ
    ВТ_ВсеДанные.Номенклатура КАК Номенклатура,
    СУММА(ВТ_ВсеДанные.Выручка) КАК Выручка,
    СУММА(ВТ_ВсеДанные.Себестоимость) КАК Себестоимость,
    СУММА(ВТ_ВсеДанные.НДС) КАК НДС,
    СУММА(ВТ_ВсеДанные.Выручка - ВТ_ВсеДанные.Себестоимость - ВТ_ВсеДанные.НДС) КАК Прибыль
ИЗ
    ВТ_ВсеДанные КАК ВТ_ВсеДанные

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

Важная деталь: Обратите внимание, что в каждом из объединяемых запросов мы создаем одинаковое количество колонок, но "зануляем" те показатели, которые не относятся к текущему счету. При финальной группировке функция СУММА аккуратно складывает эти нули с реальными значениями из других пакетов, формируя красивую итоговую строку для каждого товара.

---

Метод 3. Перенос группировки на сторону СКД (Кросс-таблица)

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

Для этого в СКД достаточно написать простейший плоский запрос, выбирающий данные "как есть":


ВЫБРАТЬ
    ХозрасчетныйОбороты.Субконто1 КАК Номенклатура,
    ХозрасчетныйОбороты.Счет КАК Счет,
    ВЫБОР
        КОГДА ХозрасчетныйОбороты.Счет.Вид = ЗНАЧЕНИЕ(ВидСчета.Активный)
            ТОГДА ХозрасчетныйОбороты.СуммаОборотДт
        ИНАЧЕ ХозрасчетныйОбороты.СуммаОборотКт
    КОНЕЦ КАК СуммаОборот
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(, , , Счет В (&СписокСчетов), , , , ) КАК ХозрасчетныйОбороты

После этого перейдем к настройке структуры отчета на вкладке "Настройки" в схеме компоновки данных:

  1. Добавим поле СуммаОборот в список Ресурсов со стандартным выражением Сумма(СуммаОборот).
  2. В структуре отчета создадим элемент типа Таблица (вместо привычной группировки "Список").
  3. В качестве Строки таблицы укажем поле Номенклатура.
  4. В качестве Колонки таблицы выберем поле Счет.

При формировании отчета СКД автоматически создаст кросс-таблицу. В строках будут выведены ваши товары (номенклатура), в колонках — счета (90.01, 90.02, 90.03), а на их пересечении отобразятся корректные суммы оборотов. Этот вариант требует минимум кода и отлично подходит для динамических отчетов, где состав счетов может меняться пользователем в настройках.

---

Архитектурная деталь: Проблема субконто «Номенклатура» на 90-м счете

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

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

Применяйте данные подходы в зависимости от требований к производительности вашей системы и архитектуры конфигурации!

← На главную