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