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