Почему запрос по отрицательным остаткам во временной таблице выдает неверный результат в СКД?

Программист 1С v8.3 (Обычные формы) 1С:Комплексная автоматизация Управленческий учет Торговля и дистрибуция
← На главную

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

Суть проблемы: когда данные «схлопываются»

Рассмотрим классическую ситуацию. Нам нужно получить список номенклатуры, по которой есть «минуса» на складах в разрезе серий. Мы пишем запрос, который сначала помещает данные во временную таблицу (ВТ), а затем выбирает из нее только номенклатуру:


ВЫБРАТЬ 
    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    ТоварыНаСкладахОстатки.СерияНоменклатуры КАК СерияНоменклатуры,
    ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
ПОМЕСТИТЬ ВТ_Остатки
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
ГДЕ
    ТоварыНаСкладахОстатки.КоличествоОстаток < 0 ;

ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВТ_Остатки.Номенклатура
ИЗ
    ВТ_Остатки

Проанализируем ожидаемое поведение: если по Товару №1 на Серии №1 числится -5 шт., а на Серии №2 числится +10 шт., то первый запрос должен найти строку с -5 и поместить ее в ВТ. В итоге мы должны увидеть этот товар в списке. Однако в СКД мы можем получить пустой результат. Почему?

Причина: интеллектуальная оптимизация запроса в СКД

Выясним причину такого поведения. Главное отличие СКД от обычного запроса (Новый Запрос) заключается в том, что СКД — это не просто исполнитель текста, а генератор запросов. Перед тем как отправить запрос на сервер СУБД, СКД анализирует, какие поля выбраны в настройках отчета (на закладке «Настройки» или пользователем).

Если в финальном результате (в группировках или выбранных полях) не используется поле СерияНоменклатуры, СКД считает его лишним. Чтобы сэкономить ресурсы, система вырезает это поле из всех частей пакета запросов, включая виртуальные таблицы.

Процесс трансформации запроса выглядит так:

  1. СКД видит, что во втором запросе нам нужна только Номенклатура.
  2. Она анализирует первый запрос и понимает, что СерияНоменклатуры и Склад не «пробрасываются» дальше в результат.
  3. СКД модифицирует обращение к виртуальной таблице Остатки, убирая из нее измерения «Серия» и «Склад».
  4. В итоге виртуальная таблица начинает суммировать остатки сразу в разрезе только номенклатуры.

В нашем примере с Товаром №1 (где было -5 и +10) СКД получит суммарный остаток +5. Условие ГДЕ КоличествоОстаток < 0 отсечет этот положительный остаток, и товар вообще не попадет во временную таблицу. Именно поэтому вместо 400 ожидаемых позиций мы видим только 4 — те, по которым общий итог в разрезе товара (без учета серий) отрицательный.

Как решить проблему и сохранить детализацию

Рассмотрим подробнее методы, которые позволяют обойти эту оптимизацию и заставить СКД учитывать нужные нам разрезы данных.

Способ 1: Использование роли «Обязательное»

Это самый «чистый» способ. В схеме компоновки данных на закладке «Наборы данных» найдите список полей вашего запроса. Для полей СерияНоменклатуры и Склад нужно настроить колонку «Роль»:

  1. Нажмите на гиперссылку в колонке «Роль».
  2. В открывшемся окне установите флажок «Обязательное».

Когда поле помечено как обязательное, СКД теряет право удалять его из текста запроса, даже если оно не выведено в отчет. Группировка в виртуальной таблице сохранится, и условие на отрицательный остаток отработает корректно по каждой серии.

Способ 2: Отключение автозаполнения

Если мы снимем флажок «Автозаполнение» в наборе данных-запросе, СКД перестанет автоматически модифицировать текст. Однако в этом случае нам придется вручную прописывать все поля, доступные для выбора, и указывать их параметры. Это трудоемкий путь, который обычно используется только в очень сложных отчетах.

Способ 3: Фиктивное использование полей

Проанализируем ситуацию: если поле должно участвовать в расчетах, оно должно быть «замечено» компоновщиком. Можно добавить эти поля в скрытую группировку или наложить на них отбор, который всегда истинен (например, СерияНоменклатуры ЕСТЬ НЕ NULL), но это считается «костылем» и менее предпочтительно, чем использование ролей.

Важное замечание про консоли запросов

Посмотрим на инструменты тестирования. Существует два типа консолей:

  1. Обычная консоль запросов: работает через объект Запрос. Она выполняет код «как есть», без модификаций. Там ваш запрос всегда будет работать «правильно».
  2. Консоль системы компоновки (консоль отчетов): она имитирует работу реального отчета. Если ваша консоль построена на базе СКД, она будет проявлять ту же «оптимизацию», что и ваш отчет.

Резюме: Поведение СКД — это не баг, а механизм повышения производительности. Чтобы данные не агрегировались раньше времени, всегда проверяйте роли полей в наборе данных, если используете условия на количественные показатели во временных таблицах.

← На главную