Почему возникает ошибка Поле не найдено при добавлении группы отборов в СКД и как её исправить?

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

При работе с Системой компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда программное добавление отдельных элементов отбора работает безупречно, но стоит объединить их в группу (например, «И» или «ИЛИ»), как система выдает ошибку «Поле не найдено». Особенно часто это проявляется в сложных запросах с использованием временных таблиц и механизмов расширений {ГДЕ ...}. В этой статье мы подробно разберем, почему так происходит, как работает внутренний механизм компоновщика настроек и какими способами можно решить эту проблему.

Разберем ситуацию на примере

Рассмотрим сценарий, описанный в вопросе. Программист динамически создает схему компоновки данных. В тексте запроса присутствуют две части: формирование временной таблицы с отбором по сегменту номенклатуры и основной запрос к остаткам. Поле СегментНоменклатуры определено только во временной таблице внутри необязательного условия {ГДЕ ...}. Поле Остаток доступно в основном запросе.

Когда отборы добавляются по отдельности в корень списка отборов, СКД проявляет свою «интеллектуальность»: она анализирует каждое условие и понимает, что отбор по сегменту нужно применить к временной таблице, а отбор по остатку — к итоговому результату. Однако при использовании группы элементов отбора эта логика меняется.

Посмотрим на фрагмент кода, который вызывает ошибку:


ГруппаОтбора = НастройкиКомпоновки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
// Условие по остатку (доступно в основном запросе)
ЭлементОтбора = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Остаток");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Больше;
ЭлементОтбора.ПравоеЗначение = 0;

// Условие по сегменту (доступно только во временной таблице через расширение ГДЕ)
ЭлементОтбора = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("СегментНоменклатуры");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = СегментДляОтбора;

Выясним причину: механизм «нерасщепляемости» групп

Главная особенность СКД заключается в том, что группа отборов (И, ИЛИ, НЕ) воспринимается компоновщиком как единый, неделимый логический блок. Если отдельные элементы отбора компоновщик может «разнести» по разным частям запроса (один в параметры виртуальной таблицы, другой в секцию ГДЕ временной таблицы, третий в итоговый запрос), то с группой он так поступить не может.

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

Важный нюанс: даже если вы используете Группу И, СКД все равно обрабатывает её как целостный объект. Несмотря на то что логически И можно было бы разделить, алгоритмы платформы требуют, чтобы все поля группы были доступны в контексте того уровня запроса, к которому применяется группа.

Решение №1: Проброс поля в итоговую выборку

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

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

  1. Выберем поле Сегмент в список полей временной таблицы.
  2. Добавим левое соединение основной таблицы с этой временной таблицей (или используем вложенный запрос).
  3. Выберем это поле в итоговом списке ВЫБРАТЬ.

После этого поле СегментНоменклатуры станет «реальным» для итогового набора данных, и СКД сможет наложить на него группу отборов в секции ГДЕ самого последнего уровня запроса.

Решение №2: Использование отдельных отборов (отказ от групп)

Если ваша задача подразумевает только логическое «И» (то есть должны выполняться оба условия одновременно), то использование Группы И часто является избыточным. По умолчанию все элементы, добавленные в корень коллекции Отбор.Элементы, соединяются по условию «И».

Проанализируем разницу в коде. Вместо создания группы, добавим элементы напрямую:


// Вместо ГруппаОтбора.Элементы.Добавить...
// Используем НастройкиКомпоновки.Отбор.Элементы.Добавить...

Элемент1 = НастройкиКомпоновки.Отбор.Эleменты.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Остаток");
// ... настройки вида сравнения ...

Элемент2 = НастройкиКомпоновки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("СегментНоменклатуры");
// ... настройки вида сравнения ...

В этом случае СКД успешно «расщепит» условия: отбор по сегменту уйдет во временную таблицу (в секцию {ГДЕ...}), а отбор по остатку применится к виртуальной таблице или итоговому запросу. Ошибка исчезнет.

Решение №3: Параметры данных как альтернатива

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

В тексте запроса в секции {ГДЕ...} можно указать использование параметра. В коде же мы будем управлять не элементами отбора, а значениями этих параметров и их использованием. Однако этот метод менее гибкий, если пользователю нужно выбирать произвольные виды сравнения (например, «В списке», «Не равно»).

Сложные случаи с логическим «ИЛИ»

Если вам жизненно необходимо условие вида (Сегмент = &Сегмент ИЛИ Остаток > 0), то вариант с разделением (Решение №2) не подойдет технически. Здесь остается только Решение №1 (проброс поля).

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

Резюме для разработчика

Для предотвращения ошибки Поле не найдено в группах отборов СКД, следуйте этим правилам:

Понимание того, как компоновщик превращает ваши настройки в итоговый SQL-текст, позволяет создавать гибкие и производительные отчеты без труднообъяснимых ошибок.

← На главную