При работе с Системой компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда один и тот же запрос выдает разные результаты в консоли запросов и в отчете. Особенно ярко это проявляется при получении остатков с условием на отрицательные значения. Разберем детально, почему так происходит, и выясним, баг это или фича платформы 1С.
Рассмотрим классическую ситуацию. Нам нужно получить список номенклатуры, по которой есть «минуса» на складах в разрезе серий. Мы пишем запрос, который сначала помещает данные во временную таблицу (ВТ), а затем выбирает из нее только номенклатуру:
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстатки.СерияНоменклатуры КАК СерияНоменклатуры,
ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
ПОМЕСТИТЬ ВТ_Остатки
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
ГДЕ
ТоварыНаСкладахОстатки.КоличествоОстаток < 0 ;
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТ_Остатки.Номенклатура
ИЗ
ВТ_Остатки
Проанализируем ожидаемое поведение: если по Товару №1 на Серии №1 числится -5 шт., а на Серии №2 числится +10 шт., то первый запрос должен найти строку с -5 и поместить ее в ВТ. В итоге мы должны увидеть этот товар в списке. Однако в СКД мы можем получить пустой результат. Почему?
Выясним причину такого поведения. Главное отличие СКД от обычного запроса (Новый Запрос) заключается в том, что СКД — это не просто исполнитель текста, а генератор запросов. Перед тем как отправить запрос на сервер СУБД, СКД анализирует, какие поля выбраны в настройках отчета (на закладке «Настройки» или пользователем).
Если в финальном результате (в группировках или выбранных полях) не используется поле СерияНоменклатуры, СКД считает его лишним. Чтобы сэкономить ресурсы, система вырезает это поле из всех частей пакета запросов, включая виртуальные таблицы.
Процесс трансформации запроса выглядит так:
Номенклатура.СерияНоменклатуры и Склад не «пробрасываются» дальше в результат.Остатки, убирая из нее измерения «Серия» и «Склад».В нашем примере с Товаром №1 (где было -5 и +10) СКД получит суммарный остаток +5. Условие ГДЕ КоличествоОстаток < 0 отсечет этот положительный остаток, и товар вообще не попадет во временную таблицу. Именно поэтому вместо 400 ожидаемых позиций мы видим только 4 — те, по которым общий итог в разрезе товара (без учета серий) отрицательный.
Рассмотрим подробнее методы, которые позволяют обойти эту оптимизацию и заставить СКД учитывать нужные нам разрезы данных.
Это самый «чистый» способ. В схеме компоновки данных на закладке «Наборы данных» найдите список полей вашего запроса. Для полей СерияНоменклатуры и Склад нужно настроить колонку «Роль»:
Когда поле помечено как обязательное, СКД теряет право удалять его из текста запроса, даже если оно не выведено в отчет. Группировка в виртуальной таблице сохранится, и условие на отрицательный остаток отработает корректно по каждой серии.
Если мы снимем флажок «Автозаполнение» в наборе данных-запросе, СКД перестанет автоматически модифицировать текст. Однако в этом случае нам придется вручную прописывать все поля, доступные для выбора, и указывать их параметры. Это трудоемкий путь, который обычно используется только в очень сложных отчетах.
Проанализируем ситуацию: если поле должно участвовать в расчетах, оно должно быть «замечено» компоновщиком. Можно добавить эти поля в скрытую группировку или наложить на них отбор, который всегда истинен (например, СерияНоменклатуры ЕСТЬ НЕ NULL), но это считается «костылем» и менее предпочтительно, чем использование ролей.
Посмотрим на инструменты тестирования. Существует два типа консолей:
Запрос. Она выполняет код «как есть», без модификаций. Там ваш запрос всегда будет работать «правильно».Резюме: Поведение СКД — это не баг, а механизм повышения производительности. Чтобы данные не агрегировались раньше времени, всегда проверяйте роли полей в наборе данных, если используете условия на количественные показатели во временных таблицах.