Как просуммировать значения нескольких реквизитов (колонок) в запросе 1С в зависимости от параметра?

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

При разработке отчетов или сложных выборок данных в 1С программисты часто сталкиваются с ситуацией, когда структура базы данных не совсем оптимальна. Например, в регистре сведений или документе данные по дням месяца разнесены в разные реквизиты: День1, День2, ..., День31. Задача состоит в том, чтобы в одном запросе получить сумму этих реквизитов с первого дня по день, указанный в параметре &НомерДня. При этом часто накладывается ограничение: запрос должен быть статичным, чтобы его можно было вставить в схему компоновки данных (СКД) без сложной программной обработки.

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

Способ №1: Использование конструкции ВЫБОР (CASE)

Это самый очевидный и понятный способ. Мы строим логику запроса, перебирая все возможные значения параметра &НомерДня. Рассмотрим пример реализации для первых нескольких дней:


ВЫБРАТЬ
    РеестрПлатежей.СтатьяБюджета,
    ВЫБОР
        КОГДА &НомерДня = 1 ТОГДА
            РеестрПлатежей.День1
        КОГДА &НомерДня = 2 ТОГДА
            РеестрПлатежей.День1 + РеестрПлатежей.День2
        КОГДА &НомерДня = 3 ТОГДА
            РеестрПлатежей.День1 + РеестрПлатежей.День2 + РеестрПлатежей.День3
        // ... и так далее до 31
        ИНАЧЕ 0
    КОНЕЦ КАК СуммаИтого
ИЗ
    РегистрСведений.РеестрПлатежей КАК РеестрПлатежей
ГДЕ
    РеестрПлатежей.НомерРеестра = &Номер

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

  1. Производительность: С точки зрения СУБД (SQL Server, PostgreSQL), такой запрос выполняется за один проход по таблице (Index Scan или Table Scan). Это максимально быстро.
  2. Простота отладки: Вы сразу видите, какая логика применяется для каждого конкретного значения параметра.

Недостатки:

  1. Громоздкость: Текст запроса получается очень длинным (31 условие).
  2. Сложность поддержки: При необходимости изменить формулу расчета придется вносить правки в десятки строк кода.

Способ №2: Разворачивание колонок в строки через ОБЪЕДИНИТЬ ВСЕ (UNION)

Если мы хотим сделать итоговое суммирование более коротким, мы можем сначала "нормализовать" данные во временной таблице. Мы переводим колонки День1, День2 и т.д. в строки с указанием индекса дня. Разберем этот алгоритм по шагам.

Сначала сформируем временную таблицу, где каждая запись — это значение конкретного дня:


ВЫБРАТЬ РеестрПлатежей.СтатьяБюджета, РеестрПлатежей.День1 КАК ЗначениеДня, 1 КАК ИндексДня
ПОМЕСТИТЬ ВТ_Дни ИЗ РегистрСведений.РеестрПлатежей КАК РеестрПлатежей ГДЕ НомерРеестра = &Номер
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ РеестрПлатежей.СтатьяБюджета, РеестрПлатежей.День2, 2
ИЗ РегистрСведений.РеестрПлатежей КАК РеестрПлатежей ГДЕ НомерРеестра = &Номер
// ... повторить для всех 31 дня
;

ВЫБРАТЬ
    ВТ_Дни.СтатьяБюджета,
    СУММА(ВТ_Дни.ЗначениеДня) КАК ИтоговаяСумма
ИЗ
    ВТ_Дни КАК ВТ_Дни
ГДЕ
    ВТ_Дни.ИндексДня <= &НомерДня
СГРУППИРОВАТЬ ПО
    ВТ_Дни.СтатьяБюджета

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

Способ №3: Математический метод (Условные множители)

Существует оригинальный способ избежать длинных веток ВЫБОР. Мы можем умножать значение каждого реквизита на 0 или 1 в зависимости от условия. В языке запросов 1С это реализуется через вложенные конструкции ВЫБОР, которые возвращают числовой коэффициент.

Посмотрим на пример кода:


ВЫБРАТЬ
    РеестрПлатежей.СтатьяБюджета,
    РеестрПлатежей.День1 * (ВЫБОР КОГДА &НомерДня >= 1 ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) +
    РеестрПлатежей.День2 * (ВЫБОР КОГДА &НомерДня >= 2 ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) +
    РеестрПлатежей.День3 * (ВЫБОР КОГДА &НомерДня >= 3 ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) +
    // ... и так до 31
    РеестрПлатежей.День31 * (ВЫБОР КОГДА &НомерДня >= 31 ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) КАК ИтоговаяСумма
ИЗ
    РегистрСведений.РеестрПлатежей КАК РеестрПлатежей

Почему это удобно? Во-первых, формула линейная. Во-вторых, она легко читается. В-третьих, если вам понадобится просуммировать только четные дни или дни в определенном диапазоне, вам достаточно просто изменить условия в скобках, не переписывая структуру всего запроса.

Способ №4: Программное формирование запроса в СКД

Если вы используете Систему компоновки данных, вам не обязательно писать все 31 условие вручную. Вы можете использовать событие ПриКомпоновкеРезультата в модуле объекта отчета. Это позволит сформировать текст запроса динамически, при этом для пользователя это останется обычным статичным отчетом.

Рассмотрим пример кода на языке 1С, который генерирует нужную строку суммы:


// В модуле объекта отчета
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Номер дня, полученный из параметров
    НужныйДень = 15; // Пример
    
    СтрокаСуммы = "";
    Для Индекс = 1 По НужныйДень Цикл
        СтрокаСуммы = СтрокаСуммы + ?(СтрокаСуммы = "", "", " + ") + "РеестрПлатежей.День" + Формат(Индекс, "ЧГ=0");
    КонецЦикла;
    
    ТекстЗапроса = "ВЫБРАТЬ " + СтрокаСуммы + " КАК Сумма ИЗ РегистрСведений.РеестрПлатежей КАК РеестрПлатежей";
    
    // Далее устанавливаем текст в схему и выполняем компоновку...
    
КонецПроцедуры

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

Рекомендации по оптимизации

Рассмотрим важные аспекты, которые стоит учитывать при выборе решения:

Проанализировав все варианты, можно сделать вывод: если важна максимальная скорость — используйте Способ №1 (CASE). Если важна краткость и чистота кода — используйте программную генерацию текста запроса в СКД (Способ №4). Математический метод (Способ №3) является отличным компромиссом для быстрого написания работающего кода без лишнего нагромождения условий ВЫБОР.

← На главную