Почему отбор в СКД по ресурсу фильтрует исходные данные, а не итоги, и как это исправить?

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

В практике разработки отчетов на Системе Компоновки Данных (СКД) мы часто сталкиваемся с неочевидным поведением платформы при наложении отборов на числовые поля. Рассмотрим классическую ситуацию: у вас есть ресурс (например, количество товара на складах), и вы хотите отобрать только те номенклатурные позиции, где общий итог соответствует определенному условию (например, `ВНаличии = 1`). Однако вместо того, чтобы показать товары с общим остатком 1, отчет выводит все товары, у которых хотя бы на одном складе (или в одной записи регистра) был остаток 1. Давайте подробно разберем, почему так происходит, и проанализируем способы решения этой задачи.

Суть проблемы: WHERE против HAVING

Чтобы понять корень проблемы, обратимся к механизму работы запросов. СКД — это мощный инструмент, который старается максимально оптимизировать получение данных. Когда вы накладываете отбор на поле, которое присутствует в наборе данных (в запросе), компоновщик по умолчанию пытается "протолкнуть" это условие на уровень СУБД в секцию GDE (SQL WHERE). Это логично для обычных полей: зачем тянуть из базы лишние строки, если их можно отсечь сразу? Но для ресурсов это поведение становится критичным. Представим таблицу остатков: * Товар А, Склад 1, Остаток: 1 шт. * Товар А, Склад 2, Остаток: 5 шт. * **Итого по Товару А: 6 шт.** Если мы установим отбор `Остаток = 1` в настройках отчета, СКД добавит условие в запрос к базе данных. Система отберет только первую строку (где остаток равен 1), а вторую отбросит. В итоге в отчете мы увидим Товар А с суммой 1, хотя на самом деле его 6. Нам же нужно, чтобы система сначала сложила 1 + 5, получила 6, проверила условие `6 = 1` и скрыла строку целиком. В терминах языка запросов нам нужно условие в секции ИМЕЮЩИЕ (SQL HAVING), которое применяется **после** группировки.

Решение 1. Отбор на уровне группировки

Самый быстрый способ, который часто помогает без изменения конфигурации схемы — это перенос отбора из общих настроек отчета (вкладка "Отбор" корневого элемента) непосредственно в настройки группировки. Разберем алгоритм действий: 1. Открываем настройки варианта отчета. 2. В дереве группировок выбираем ту группировку, по которой рассчитывается итог (например, "Номенклатура"). 3. Переходим на вкладку "Отбор" **именно для этой группировки**. 4. Добавляем наше условие: ВНаличии Равно 1. В этом случае СКД понимает, что мы хотим фильтровать уже сгруппированные данные. Поскольку поле ВНаличии является ресурсом (определено на вкладке "Ресурсы" схемы СКД), система применит конструкцию HAVING к результату группировки. **Важный нюанс:** Этот метод работает отлично, если структура отчета статична. Если же пользователь захочет изменить структуру (убрать группировку), отбор может перестать работать или отработать некорректно.

Решение 2. Разделение полей данных и полей отбора

Если нам необходимо предоставить пользователю возможность накладывать отбор в "шапке" отчета (в быстрых настройках), но при этом гарантировать фильтрацию по итогам, мы воспользуемся механизмом ограничений полей. Давайте настроим схему так, чтобы запретить СКД использовать поле ресурса в секции ГДЕ. 1. **Создаем дубль поля.** На вкладке "Вычисляемые поля" создадим новое поле, например ПолеДляОтбораОстатка. В выражение пути данных пропишем путь к нашему ресурсу: ВНаличии (или имя вашего поля из набора данных). 2. **Настраиваем ограничения.** Перейдем на вкладку "Наборы данных". Найдем наше исходное поле ВНаличии. В правой части окна есть колонки ограничений. Нас интересует колонка "Условие" (значок воронки). Установим галочку в этой колонке для поля ВНаличии. **Что мы сделали:** Мы запретили компоновщику использовать поле ВНаличии в условии запроса (в секции ГДЕ). 3. **Настраиваем отбор.** Теперь, когда пользователь будет накладывать отбор на поле ПолеДляОтбораОстатка (или даже на само поле ресурса, если мы правильно настроили выражения ресурсов), СКД не сможет "спустить" отбор в запрос к базе данных, так как мы это запретили. Системе придется сначала выполнить запрос, получить все данные, сгруппировать их, и только потом, на уровне компоновки результата, применить фильтр к рассчитанным значениям ресурсов.

Решение 3. Использование вложенного запроса

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


ВЫБРАТЬ
    ВложенныйЗапрос.Номенклатура,
    ВложенныйЗапрос.ВНаличии
ИЗ
    (ВЫБРАТЬ
        ТоварыНаСкладах.Номенклатура КАК Номенклатура,
        СУММА(ТоварыНаСкладах.ВНаличии) КАК ВНаличии
    ИЗ
        РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладах
    СГРУППИРОВАТЬ ПО
        ТоварыНаСкладах.Номенклатура) КАК ВложенныйЗапрос

В этом случае, даже если СКД применит условие ГДЕ ВНаличии = 1 к внешнему запросу, это будет корректно, так как ВложенныйЗапрос уже содержит просуммированные данные.

Решение 4. Управление расширением языка запросов

В исходной теме обсуждалась проблема с временными таблицами. СКД использует инструкции в фигурных скобках {ГДЕ ...} для вставки отборов пользователя. Если ваш запрос выглядит так:


ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Данные
ИЗ ...
{ГДЕ
    ПолеРесурса КАК ПолеРесурса}

То любой отбор по ПолеРесурса будет внедрен прямо в момент формирования временной таблицы. Чтобы избежать этого: 1. Уберите поле ресурса из списка полей, доступных в инструкции {ГДЕ ...} внутри запроса формирования временных таблиц. 2. Если вы используете автозаполнение, попробуйте отключить его для конкретного набора данных и прописать поля вручную, исключив ресурс из доступных для отбора на уровне запроса.

Сводная таблица действий

Подведем итог. Если вы столкнулись с фильтрацией детальных записей вместо итогов: 1. Проверьте, занесено ли поле на вкладку **"Ресурсы"**. Без этого СКД не знает, что это поле нужно суммировать, а не просто выводить. 2. Попробуйте установить отбор не в "Отчете", а в конкретной **"Группировке"**. 3. Если нужен отбор в шапке — используйте **Вычисляемое поле** и ограничение использования исходного поля в условии (на вкладке "Наборы данных"). 4. В крайнем случае — перепишите запрос, используя **вложенный запрос** с группировкой, чтобы СКД получала готовые итоги.

Используя эти методы, мы можем гибко управлять поведением отчета и гарантировать, что пользователь увидит именно те данные, которые ожидает, без искажений логики агрегации.

← На главную