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