Почему в СКД при объединении запросов пропадают данные при наложении отбора в 1С 8.3.13 и как это исправить?

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

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

В этой статье мы подробно разберем, почему изменилось поведение платформы, как работает новый механизм оптимизации отборов СКД, из-за которого пропадают данные, и рассмотрим несколько практических способов обхода этой проблемы как для версии 8.3.13, так и для последующих релизов платформы (8.3.14 и выше).

Выясняем причину: что изменилось в механизме СКД 8.3.13

Рассмотрим классический пример запроса, который часто используется в отчетах по продажам и возвратам. Нам необходимо получить список документов продажи с возможностью фильтрации по складу, а также список документов возврата (в которых склада в качестве реквизита может не быть вовсе или отбор по нему сознательно не должен применяться):


ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    РеализацияТоваровУслуг.Дата МЕЖДУ &Дата1 И &Дата2
{ГДЕ
    РеализацияТоваровУслуг.Склад.*}

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    ВозвратТоваровОтПокупателя.Ссылка
ИЗ
    Документ.ВозвратТоваровОтПокупателя КАК ВозвратТоваровОтПокупателя
ГДЕ
    ВозвратТоваровОтПокупателя.Дата МЕЖДУ &Дата1 И &Дата2

Проанализируем, как выполнялся этот запрос на разных версиях платформы:

  1. В версии платформы 8.3.12: Если пользователь устанавливал отбор по конкретному складу, СКД накладывала это условие только на первую часть запроса (на документы реализации). Вторая часть запроса (возвраты) выполнялась без отбора по складу. На выходе мы получали корректный результат: отфильтрованные реализации и все возвраты за период.
  2. В версии платформы 8.3.13: Разработчики платформы решили унифицировать логику применения отборов между «Набором данных - Объединение» и обычным «Набором данных - Запрос», содержащим инструкцию ОБЪЕДИНИТЬ. Теперь, если отбор применяется хотя бы к одной из частей объединения, СКД принудительно транслирует этот отбор во все части объединения в результирующем SQL-запросе.

Поскольку во второй части нашего запроса (выборка из возвратов) поля Склад нет, СКД генерирует в итоговом SQL-запросе для этой ветки условие вида:

ГДЕ NULL = &ПараметрОтбораСклад

Согласно стандартам работы СУБД, любое сравнение с NULL (будь то равенство, неравенство или проверка вхождения в список) возвращает значение ложности (или неопределенности). В результате вся ветка с возвратами полностью отсекается на уровне СУБД, и пользователь видит в отчете исключительно документы реализации. Данные по возвратам бесследно исчезают из финального результата.

Этот косяк проявляется независимо от того, включено ли у вас «Автозаполнение» в настройках набора данных или нет.

---

Способ 1. Использование временных таблиц в пакете запросов (для 8.3.13)

Если ваша конфигурация работает на платформе 8.3.13 и вы не можете оперативно обновить платформу или изменить режим совместимости, одним из самых надежных способов решения проблемы является разделение логики объединения с помощью временных таблиц (ВТ). Это не позволит оптимизатору СКД «дотянуться» своими отборами до тех частей запроса, где их быть не должно.

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


ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Реализации
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    РеализацияТоваровУслуг.Дата МЕЖДУ &Дата1 И &Дата2
{ГДЕ
    РеализацияТоваровУслуг.Склад.*}
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВозвратТоваровОтПокупателя.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Возвраты
ИЗ
    Документ.ВозвратТоваровОтПокупателя КАК ВозвратТоваровОтПокупателя
ГДЕ
    ВозвратТоваровОтПокупателя.Дата МЕЖДУ &Дата1 И &Дата2
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Реализации.Ссылка КАК Ссылка
ИЗ
    ВТ_Реализации КАК ВТ_Реализации

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

ВЫБРАТЬ
    ВТ_Возвраты.Ссылка
ИЗ
    ВТ_Возвраты КАК ВТ_Возвраты

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

---

Способ 2. Объединение на уровне структуры СКД (Набор данных - Объединение)

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

Разберем этот метод по шагам:

  1. Откроем схему компоновки данных нашего отчета.
  2. Удалим текущий единый «Набор данных - запрос».
  3. Добавим новый набор данных с типом «Набор данных - объединение» и назовем его, например, ОбъединениеДанных.
  4. Внутри этого объединения создадим два подчиненных набора с типом «Набор данных - запрос»:
    • НаборДанныхРеализации — сюда поместим только первую часть запроса (выборку документов реализации с полем Склад).
    • НаборДанныхВозвраты — сюда поместим вторую часть запроса (выборку документов возврата без поля Склад).
  5. На вкладке «Настройки» СКД добавим глобальный отбор по полю Склад.

При такой архитектуре СКД применит пользовательский отбор только к первому набору данных (где поле Склад присутствует в списке доступных полей). Второй набор данных выполнится без ограничений, после чего СКД корректно объединит результаты обоих наборов на уровне процессора компоновки.

---

Способ 3. Передача отбора через параметры с обработкой NULL (Альтернативный вариант)

Если вам по каким-то причинам не подходят временные таблицы и создание нескольких наборов данных, вы можете заменить стандартный отбор СКД (в фигурных скобках) на явное условие с использованием параметров данных.

Для этого в первой части запроса жестко пропишем условие по параметру, а во второй части запроса сделаем заглушку, которая будет игнорировать этот параметр:


ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    РеализацияТоваровУслуг.Дата МЕЖДУ &Дата1 И &Дата2
    И (&ИспользоватьОтборПоСкладу = ЛОЖЬ ИЛИ РеализацияТоваровУслуг.Склад = &Склад)

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

ВЫБРАТЬ
    ВозвратТоваровОтПокупателя.Ссылка
ИЗ
    Документ.ВозвратТоваровОтПокупателя КАК ВозвратТоваровОтПокупателя
ГДЕ
    ВозвратТоваровОтПокупателя.Дата МЕЖДУ &Дата1 И &Дата2

В настройках СКД на вкладке «Параметры» мы должны настроить параметр &ИспользоватьОтборПоСкладу (например, вычислять его выражение как ЗначениеЗаполнено(ПараметрыДанных.Склад)). Данный подход требует написания дополнительного кода или настройки выражений параметров в СКД, но гарантирует стабильность работы запроса в любых версиях платформы.

---

Способ 4. Как вернуть поведение 8.3.12 на платформе 8.3.14 и выше

Разработчики фирмы «1С» признали неудобство принудительной унификации отборов в релизе 8.3.13 и в платформе 8.3.14 внесли изменения. Они вернули возможность изолировать отборы в разных частях объединения, но сделали это поведение опциональным.

Чтобы восстановить логику работы платформы 8.3.12 на современных релизах платформы (при условии, что режим совместимости конфигурации установлен в значение 8.3.14 или выше), выполните следующие действия:

  1. Откройте схему компоновки данных вашего отчета.
  2. Перейдите к нашему набору данных (запросу с ОБЪЕДИНИТЬ).
  3. Обязательно снимите флаг «Автозаполнение» (находится на панели настроек набора данных).
  4. В тексте запроса вручную опишите доступные поля отбора с использованием расширения языка запросов СКД (фигурных скобок) только в той части объединения, где этот отбор действительно необходим:

ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    РеализацияТоваровУслуг.Дата МЕЖДУ &Дата1 И &Дата2
{ГДЕ
    РеализацияТоваровУслуг.Склад.* КАК Склад}

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

ВЫБРАТЬ
    ВозвратТоваровОтПокупателя.Ссылка
ИЗ
    Документ.ВозвратТоваровОтПокупателя КАК ВозвратТоваровОтПокупателя
ГДЕ
    ВозвратТоваровОтПокупателя.Дата МЕЖДУ &Дата1 И &Дата2

Обратите внимание на конструкцию КАК Склад в первой секции {ГДЕ ...}. Она явно связывает отбор по складскому реквизиту с полем компоновки. Поскольку автозаполнение отключено, СКД не будет анализировать вторую таблицу (возвраты) на предмет наличия этого поля и не станет подставлять туда условие NULL = &Склад. Данные по возвратам будут выводиться в полном объеме, даже если пользователь установит отбор по складу для документов реализации.

← На главную