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