Как убрать лишние отборы в СКД при использовании объединений и виртуальных таблиц

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

При работе с Системой компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда "умные" алгоритмы платформы пытаются оптимизировать запрос, добавляя условия отбора туда, где они логически не нужны. Особенно остро эта проблема проявляется в запросах с объединениями (ОБЪЕДИНИТЬ ВСЕ) и при использовании виртуальных таблиц, таких как СрезПоследних или Обороты. Разберем подробно, почему это происходит и как заставить СКД работать строго по нашему сценарию.

Суть проблемы: механизм "проталкивания" отборов

Рассмотрим ситуацию, когда в запросе есть две ветки объединения. В первой ветке мы хотим предоставить пользователю возможность фильтровать данные по Виду цены, а во второй ветке — жестко зашить конкретные предопределенные виды цен. Однако СКД видит, что в обеих частях UNION фигурирует поле с одним и тем же смысловым значением, и при наложении пользователем отбора "проталкивает" его (Filter Push-down) во все части запроса, включая те, где этот отбор нарушает логику получения данных.

Проанализируем типичный пример проблемного запроса:


ВЫБРАТЬ
    Цены.Номенклатура,
    Цены.ВидЦены,
    Цены.Цена
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, {(ВидЦены)}) КАК Цены
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    ЦеныСпец.Номенклатура,
    ЦеныСпец.ВидЦены,
    ЦеныСпец.Цена
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ЦенаСпец) КАК ЦеныСпец

Даже если мы не указали фигурные скобки {} во второй части запроса, СКД при включенном автозаполнении может добавить условие И ВидЦены В (&П) во вторую виртуальную таблицу, если пользователь установил отбор по виду цены в настройках отчета. Это происходит из-за того, что имена полей в итоговой выборке совпадают.

Способ 1. Отключение автозаполнения и ручная настройка

Самый надежный, но требующий внимательности метод — отключение флажка Автозаполнение на закладке "Наборы данных". Выясним, как это работает:

  1. Снимем галочку Автозаполнение в конструкторе схемы компоновки.
  2. Перейдем на закладку Компоновка данных внутри конструктора запроса.
  3. Самостоятельно определим, какие поля будут доступны для выбора, какие для отбора, а какие для порядка.
  4. В секции параметров виртуальных таблиц используем расширение языка запросов СКД (фигурные скобки) только там, где отбор действительно нужен.

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


РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, { (Номенклатура).* КАК Номенклатура, (ВидЦены).* КАК ВидЦены } )

Способ 2. Использование псевдонимов (алиасов) для изоляции полей

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


ВЫБРАТЬ
    Т.Номенклатура КАК Номенклатура,
    Т.ВидЦены КАК ВидЦены, -- Основное поле для отбора
    Т.Цена КАК Цена
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, {(ВидЦены)}) КАК Т

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Т.Номенклатура,
    Т.ВидЦены КАК ВидЦены_Служебный, -- Псевдоним отличается, отбор не "приклеится"
    Т.Цена
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &Цена1) КАК Т

Чтобы в итоговом отчете данные все равно выводились в одну колонку, на закладке Наборы данных в колонке Поле нужно оставить технические имена (ВидЦены и ВидЦены_Служебный), а в колонке Путь к данным для обоих полей указать одинаковое имя (например, ВидЦены). Однако этот метод может быть нестабилен, поэтому лучше использовать явное переименование с отключением доступности поля для условий.

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

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


// В первой временной таблице получаем данные с пользовательским отбором
ВЫБРАТЬ
    Цены.Номенклатура,
    Цены.ВидЦены,
    Цены.Цена
ПОМЕСТИТЬ ВТ_ПользовательскиеЦены
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен, {(Номенклатура), (ВидЦены)}) КАК Цены
;

// Во второй временной таблице получаем данные по фиксированным условиям
ВЫБРАТЬ
    ЦеныСпец.Номенклатура,
    ЦеныСпец.ВидЦены КАК ВидЦеныСпец,
    ЦеныСпец.Цена
ПОМЕСТИТЬ ВТ_ФиксированныеЦены
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен, ВидЦены В (&СписокЦен) {(Номенклатура)}) КАК ЦеныСпец
;

// Собираем результат
ВЫБРАТЬ
    Т.Номенклатура,
    Т.ВидЦены,
    Т.Цена
ИЗ ВТ_ПользовательскиеЦены КАК Т
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    Т.Номенклатура,
    Т.ВидЦеныСпец,
    Т.Цена
ИЗ ВТ_ФиксированныеЦены КАК Т

В данном примере для второй таблицы мы намеренно не указываем ВидЦены в фигурных скобках расширения параметров. Благодаря тому, что данные сначала помещаются во временную таблицу, СКД сложнее "протолкнуть" глобальный отбор внутрь первого пакета запросов, так как структура полей становится более жесткой.

Тонкая настройка ролей полей

Важно помнить про закладку Наборы данных в схеме СКД. Даже если запрос написан идеально, настройки колонок могут спровоцировать лишние отборы. Обратим внимание на следующие настройки:

Резюме

Чтобы победить "лишние" отборы в СКД, следует придерживаться правила: чем сложнее запрос, тем меньше нужно полагаться на автозаполнение. Использование временных таблиц с четко определенными псевдонимами полей и явное описание доступных условий в фигурных скобках {} позволяют создавать предсказуемые и быстрые отчеты, где данные не исчезают из-за того, что система решила оптимизировать запрос за вас.

← На главную