Почему возникает ошибка «Нельзя использовать производные от полей составного типа» в запросах 1С и как ее исправить?

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

При разработке сложных отчетов или выборок в системе 1С:Предприятие 8.3, программисты часто сталкиваются со специфической ошибкой парсера запросов: «Нельзя использовать производные от полей составного типа в выражениях, не присутствующих полностью в предложении СГРУППИРОВАТЬ ПО...». Эта проблема особенно коварна, так как на первый взгляд запрос кажется логически верным, а все поля, используемые в секции ВЫБРАТЬ, вроде бы указаны в секции СГРУППИРОВАТЬ ПО. Чтобы своевременно выявлять такие изъяны, полезно использовать статический анализатор кода проектов. Рассмотрим подробнее, почему это происходит и как правильно составить запрос, чтобы избежать конфликтов с внутренним оптимизатором платформы.

Разберем причину возникновения ошибки

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

Что такое производное поле в контексте 1С? Это любое поле, полученное путем обращения через точку к объекту. Например, Контрагент.Ссылка, Номенклатура.Код или, как в нашем примере, данные.группировка.Ссылка. Если поле группировка имеет составной тип (может содержать ссылки на разные справочники или документы), платформа при трансляции запроса в SQL создает сложные конструкции с использованием LEFT JOIN и операторов CASE для каждой таблицы, входящей в состав типа.

Проблема заключается в работе валидатора запросов 1С. Когда мы пишем:


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

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

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

Наиболее правильным и надежным способом решения, соответствующим стандартам SQL, является вынос условия ВЫБОР внутрь агрегатных функций. Вместо того чтобы выбирать, какую функцию применить к результату, мы применяем функции к результатам условий. Чтобы быстро проверить результат такого изменения, отлично подойдет консоль запросов для управляемых форм. Рассмотрим, как переработать запрос:


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

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

Способ 2: Использование типизации через ВЫРАЗИТЬ

Если мы точно знаем, что в определенном условии нас интересует конкретный тип из состава составного поля, мы можем помочь парсеру 1С, явно ограничив тип. Использование функции ВЫРАЗИТЬ снимает неопределенность составного типа и позволяет оптимизатору корректно сопоставить поля. Проанализируем пример:


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

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

Способ 3: Разделение на временные таблицы

Если запрос слишком сложный, а условий много, загромождение секции ВЫБРАТЬ вложенными конструкциями ВЫБОР может сделать код нечитаемым. В такой ситуации мы рекомендуем разделить логику на несколько шагов с использованием временных таблиц (ВТ):

  1. Создадим ВТ, в которой вычислим все необходимые признаки (флаги) для группировки. На этом этапе мы уже «разберем» составной тип и получим простые поля (числа или булево).
  2. В основном запросе выполним группировку по этим простым полям.

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

Почему обращение к .Ссылка избыточно?

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

Попробуйте заменить:
данные.группировка.Ссылка = 1
на сравнение с самим полем:
данные.группировка = &ЗначениеДляСравнения

Однако помните, что сравнение ссылки с числом (как в исходном примере темы) в принципе является некорректным с точки зрения типов данных 1С и может приводить к непредсказуемым результатам в зависимости от используемой СУБД.

Заключение

Подводя итог, отметим: ошибка «Нельзя использовать производные от полей составного типа...» — это защитный механизм платформы 1С, предотвращающий выполнение неоднозначных запросов. Чтобы успешно решить задачу, придерживайтесь правила: не смешивайте в одной конструкции ВЫБОР логику определения условий по полям через точку и агрегатные функции. Используйте агрегацию поверх условий или предварительно подготавливайте данные во временных таблицах, избавляясь от составных типов там, где это возможно.

← На главную