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

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

При разработке сложных отчетов на системе компоновки данных (СКД) программисты часто сталкиваются с необходимостью многократного использования результатов одних вычислений в других. Например, когда нам нужно сначала вычислить некий коэффициент, затем использовать его для получения дельты, а на основе этой дельты рассчитать процент. Прямая попытка указать имя одного вычисляемого поля в выражении другого, как правило, приводит к ошибке "Поле не найдено". Рассмотрим подробнее, почему так происходит, и разберем эффективные способы обхода этого ограничения.

Причина ограничения: жизненный цикл компоновки данных

Для начала проанализируем ситуацию с точки зрения архитектуры СКД. Система выполняет расчеты строго в определенном порядке. Вычисляемые поля рассчитываются на этапе получения детальных записей, но они находятся «на одном уровне». Это означает, что в момент расчета Поле2 система еще не может гарантировать, что Поле1 уже рассчитано и доступно для обращения по имени. Пользовательские поля и вовсе рассчитываются в последнюю очередь, когда выборка данных уже практически сформирована.

Важно запомнить: в режиме конфигуратора вычисляемые поля не «видят» друг друга по именам. Пользовательские поля могут видеть вычисляемые поля только в том случае, если те добавлены в выбранные поля отчета, но и здесь поведение системы может быть нестабильным.

Решение 1: Перенос логики на уровень запроса (Nested Queries)

Самый надежный и производительный способ организовать «цепочку» — это подготовить данные на уровне SQL-запроса в схеме компоновки данных. Вместо того чтобы городить формулы в настройках СКД, мы можем использовать вложенные запросы или временные таблицы.

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


ВЫБРАТЬ
    Подзапрос.Длина1 КАК Длина1,
    Подзапрос.Длина2 КАК Длина2,
    Подзапрос.Длина2 - Подзапрос.Длина1 КАК Дельта
ИЗ
    (ВЫБРАТЬ
        ТвойИсточник.Значение1 КАК Длина1,
        ТвойИсточник.Значение2 КАК Длина2
    ИЗ
        Справочник.ТвойСправочник КАК ТвойИсточник) КАК Подзапрос

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

Решение 2: Использование функции ВычислитьВыражение

Если по каким-то причинам изменить запрос нельзя, проанализируем возможности функций языка выражений СКД. Функция ВычислитьВыражение позволяет обращаться к значениям полей в контексте всей компоновки. Хотя автор темы столкнулся с «тормозами» при ее использовании, в ряде случаев это единственный путь без изменения структуры данных.

Синтаксис обращения будет выглядеть следующим образом:

ВычислитьВыражение("Сумма(ИмяПоля)", , "Общая")

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

Решение 3: Цепочки через ресурсы отчета

Малоизвестный, но эффективный лайфхак заключается в использовании вкладки «Ресурсы». В отличие от вычисляемых полей, ресурсы в СКД могут зависеть друг от друга. Если мы определим Показатель1 как ресурс и Показатель2 как ресурс, то в выражении для Показатель3 мы можем легально использовать суммы первых двух.

Например, на закладке «Ресурсы» укажем:

  1. Имя поля: Длина1; Выражение: Сумма(Длина1)
  2. Имя поля: Дельта; Выражение: Сумма(Длина2) - Сумма(Длина1)

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

Проблема типов данных и математических операций

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

Проанализируем примеры правильного написания выражений:

Пример корректного выражения для пользовательского поля:

РАЗНОСТЬДАТ([Дата начала], [Дата окончания], "ДЕНЬ")

Оптимизация вычислений через функции общих модулей

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

Рассмотрим пример кода в общем модуле:


Перем КэшРезультатов Экспорт;

Функция ПолучитьЧислоИзИмениФайла(ИмяФайла) Экспорт
    Если КэшРезультатов = Неопределено Тогда
        КэшРезультатов = Новый Соответствие;
    КонецЕсли;
    
    Результат = КэшРезультатов.Получить(ИмяФайла);
    Если Результат <> Неопределено Тогда
        Возврат Результат;
    КонецЕсли;
    
    // Здесь логика разбора строки
    Результат = ВычислитьЗначение(ИмяФайла); 
    
    КэшРезультатов.Вставить(ИмяФайла, Результат);
    Возврат Результат;
КонецФункции

В самой СКД в вычисляемом поле мы просто вызываем: ИмяМодуля.ПолучитьЧислоИзИмениФайла(ИмяФайла). Благодаря кэшированию, даже если поле используется в цепочке несколько раз, тяжелый алгоритм разбора выполнится только один раз для каждой уникальной строки.

Резюме и рекомендации

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

  1. Приоритет запросу: все, что можно вычислить в ГДЕ или ВЫБРАТЬ основного запроса через вложенные подзапросы, должно быть вычислено там.
  2. Имена в квадратных скобках: при работе в режиме «1С:Предприятие» (пользовательские поля), обращайтесь к заголовкам полей, заключая их в квадратные скобки [Заголовок поля], но предварительно убедитесь, что они выбраны в отчете.
  3. Принудительное приведение типов: используйте ВЫРАЗИТЬ(... КАК Число) или ЕстьNull, чтобы избежать ошибок арифметических операций.
  4. Ресурсы для итогов: используйте зависимости между ресурсами для вычисления сложных кросс-показателей в подвалах группировок.

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

← На главную