При разработке сложных отчетов на базе Системы компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда в пакете запросов используется несколько временных таблиц. Если в разных таблицах присутствуют поля с одинаковыми именами (например, Идентификатор, Номенклатура или Подразделение), СКД по умолчанию считает их одной и той же сущностью. Если при этом типы данных в этих полях различаются (например, в одной таблице это СправочникСсылка, а в другой — Строка), наложение стандартного отбора пользователем приводит к полной очистке результата во второй таблице. Это происходит потому, что оптимизатор СКД пытается наложить условие ГДЕ сразу на все таблицы пакета, где встречается данное имя поля.
В этой статье мы подробно разберем, почему это происходит, и рассмотрим несколько эффективных способов решения проблемы: от настройки псевдонимов до управления расширениями языка запросов.
Разберем ситуацию изнутри. Когда в схеме компоновки данных включена опция «Автозаполнение», система берет на себя задачу анализа текста запроса. СКД «просматривает» все временные таблицы и итоговый запрос. Если она находит поле, которое пользователь выбрал в настройках отбора, она автоматически модифицирует текст SQL-запроса, добавляя условия фильтрации во все части пакета, где это поле фигурирует.
Проблема заключается в том, что для СУБД сравнение ссылки с пустой строкой или строковым представлением кода может быть синтаксически корректным, но логически всегда давать ложный результат (false). В итоге временная таблица, которая не должна была фильтроваться этим условием, оказывается пустой, и весь отчет не выводит данных. Рассмотрим, как это обойти.
Самый надежный способ, который помог автору темы, — это отказ от автоматического управления полями. Проанализируем этот процесс по шагам:
Товар, а для второй — ТоварТекст.Таким образом, когда пользователь наложит отбор на поле Товар, СКД применит его только к тем частям запроса, где явно указана связь с этим псевдонимом, и проигнорирует вторую таблицу.
Если мы не хотим полностью отключать автозаполнение (так как это заставит нас вручную описывать десятки других полей), можно воспользоваться конструкциями в фигурных скобках {}. Рассмотрим этот метод подробнее.
Расширение языка запросов СКД позволяет точечно управлять местами наложения отборов. Если мы хотим, чтобы условие накладывалось только в основном запросе, а временные таблицы оставались «нетронутыми», мы можем явно прописать секцию ГДЕ в расширении:
ВЫБРАТЬ
ТЗ_Данные.Номенклатура КАК Номенклатура,
ТЗ_Данные.Количество КАК Количество
ИЗ
ВТ_ПервичныеДанные КАК ТЗ_Данные
{ГДЕ
ТЗ_Данные.Номенклатура.* КАК Номенклатура}
В этом примере мы указываем системе, что отбор по номенклатуре должен применяться именно здесь. Если в других временных таблицах пакета поле также называется Номенклатура, но оно не описано в их собственных секциях {ГДЕ}, и автозаполнение для этих таблиц настроено специфичным образом, СКД не будет навязывать им фильтр.
Проанализируем ситуацию, когда мы не хотим менять основной текст запроса, но нам нужно разделить логику полей. В конструкторе запроса СКД есть специальная вкладка «Компоновка данных». Она содержит три подвкладки: «Поля», «Условия» и «Таблицы».
Рассмотрим, как использовать вкладку «Условия»:
Это позволяет оставить визуально одинаковые имена в SQL-запросе (для удобства чтения или объединения), но сделать их абсолютно разными сущностями для генератора отчетов СКД.
Посмотрим на проблему с точки зрения пользовательского интерфейса. В схеме компоновки на вкладке «Наборы данных» есть колонка с иконкой «перечеркнутой воронки» — «Ограничение отбора».
Если мы создали два поля (например, Дата из первой таблицы и ДатаСтрокой из второй), мы можем установить флажок ограничения отбора для поля ДатаСтрокой. В этом случае:
Важно понимать, что СКД старается «пробросить» фильтры во временные таблицы как можно раньше. Это критически важно для производительности. Если в основной таблице 10 миллионов записей, а пользователь хочет отчет по одному контрагенту, СКД добавит ГДЕ Контрагент = &Параметр прямо в первый запрос, создающий временную таблицу.
Рассмотрим пример кода, где это может вызвать ошибку типов:
// ВТ_Данные1 - здесь Поле1 имеет тип СправочникСсылка.Организации
ВЫБРАТЬ Поле1, Сумма ИЗ Документ.Реализация КАК Реализация;
// ВТ_Данные2 - здесь Поле1 имеет тип Строка (например, ГУИД или код)
ВЫБРАТЬ Поле1, Комментарий ИЗ РегистрСведений.ДополнительныеСведения КАК Сведения;
Если СКД увидит в настройках отбор по Организации, она добавит условие во второй запрос: ГДЕ Сведения.Поле1 = &ПараметрОрганизация. Так как Поле1 во второй таблице — строка, СУБД вернет пустой результат. Чтобы этого избежать, используйте псевдонимы компоновки, как было описано выше. Для второй таблицы в конструкторе СКД (вкладка «Компоновка данных») укажите для Поле1 путь к данным: Поле1_Строка.
Проанализировав все способы, можно сделать вывод: наиболее «чистым» решением является явное управление полями компоновки. Не полагайтесь на «Автозаполнение», если ваш пакет запросов сложнее одного простого выбора из одной таблицы.
Основные рекомендации:
{} для точечного управления местами фильтрации.Соблюдение этих правил позволит вам создавать гибкие и производительные отчеты, избегая трудноуловимых ошибок с «исчезновением» данных из-за некорректной работы оптимизатора запросов.