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