Почему СКД подменяет значения ресурсов и как это исправить?

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

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

Анализируем причину: магия внутреннего оптимизатора СКД

Основная причина подмены полей кроется не в ошибке вашего кода, а во внутреннем механизме генерации итогового текста запроса. СКД — это не просто «пускатель» запросов, это интеллектуальный генератор SQL. Перед тем как отправить запрос на сервер СУБД, система анализирует его и пытается максимально упростить.

Проанализируем ситуацию: если в вашем запросе используются временные таблицы (ВТ) и из них выбираются поля с похожими выражениями или псевдонимами, оптимизатор может ошибочно посчитать их идентичными. В целях экономии ресурсов он «выбрасывает» одно из полей и заменяет все ссылки на него ссылками на другое поле, которое он счел эквивалентным. Это называется механизмом отсечения неиспользуемых полей, и иногда он срабатывает слишком агрессивно.

Решение 1: Преобразование временных таблиц во вложенные запросы

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

Когда мы используем ВременныеТаблицы, СКД имеет больше свободы в манипуляции их структурой. Переход к вложенным запросам заставляет генератор запросов жестко следовать иерархии выборки. Система перестает пытаться «оптимизировать» передачу данных между этапами запроса, так как структура вложенного запроса диктует четкие правила получения данных здесь и сейчас.

Выясним, как это реализовать на практике. Если ваш запрос выглядел так:


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Данные.Номенклатура,
    ВТ_Данные.КоличествоТовара,
    ВТ_Данные.КоличествоЧеков
ИЗ
    ВТ_Данные КАК ВТ_Данные

Попробуйте переписать его, исключив ПОМЕСТИТЬ ВТ_Данные и вставив этот код как подзапрос в секцию ИЗ главного запроса. Это лишит оптимизатор возможности перепутать метаданные полей на уровне временных таблиц СУБД.

Решение 2: Программное отключение оптимизатора

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

В модуле объекта отчета или в обработке, где вызывается компоновка, найдем метод КомпоновщикМакета.Выполнить. У этого метода есть параметры, один из которых отвечает за использование оптимизатора. Посмотрим на пример кода:


КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
// Параметр "ВозможностьИспользованияОптимизатора" устанавливаем в Ложь
МакетКомпоновки = КомпоновщикМакета.Выполнить(
    СхемаКомпоновкиДанных, 
    КомпоновщикНастроек.ПолучитьНастройки(), 
    ДанныеРасшифровки, 
    , 
    Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"), 
    Ложь // Вот этот важный параметр!
);

Установка этого флага в значение Ложь заставит систему выполнить запрос в том виде, в котором вы его написали в конструкторе, без попыток «вырезать» лишнее. Имейте в виду, что это может немного снизить производительность, но гарантирует точность данных.

Решение 3: Проверка ролей полей и псевдонимов

Проанализируем настройки полей на закладке «Наборы данных». Иногда «магия» подмены происходит из-за неправильно настроенных ролей. Если у двух полей, например КоличествоТовара и КоличествоЧеков, указаны одинаковые параметры в колонке «Роль» (например, они оба помечены как ключевые поля или имеют одинаковое имя в поле «Выражение детальных записей»), СКД может объединить их в один поток вычислений.

Важные моменты для проверки:

  1. Уникальность псевдонимов: Убедитесь, что во всех временных таблицах псевдонимы полей уникальны. Вместо простого Количество используйте Количество_Чеков и Количество_Товара.
  2. Флаг «Обязательное»: Если поле не выведено в настройках отчета напрямую, но участвует в расчетах других полей, установите для него флаг Обязательное. Это предотвратит его исключение оптимизатором.
  3. Автозаполнение: Если включен режим Автозаполнение, система сама расставляет роли. Попробуйте отключить его и настроить роли вручную, особенно для полей, участвующих в объединениях (LEFT JOIN).

Решение 4: Условное оформление и макеты

Иногда подмена происходит на уровне визуализации. Рассмотрим ситуацию с условным оформлением. Проверьте закладку «Условное оформление» как в самой схеме (вкладка «Настройки»), так и в настройках варианта отчета. Существует вероятность, что там настроена подмена текста для одного из полей. Например, для поля КоличествоТовара в колонке «Текст» может быть случайно выбрано значение другого поля.

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

  1. Полностью удалить набор данных в схеме СКД.
  2. Сохранить схему.
  3. Заново создать набор данных и вставить текст запроса.

Это принудительно обновит дерево доступных полей и связей, удалив «призраки» старых метаданных из памяти системы.

Подведем итоги

Проблема, когда СКД выдает одно поле за другое — это классический конфликт между вашим намерением и логикой оптимизатора 1С. Разберем краткий алгоритм действий при возникновении такой беды:

  1. Уникализируйте псевдонимы во всех частях запроса.
  2. Проверьте роли полей в наборе данных, убедитесь, что они не дублируются.
  3. Если проблема остается — замените временные таблицы на вложенные запросы.
  4. В крайнем случае — отключите оптимизатор программно через параметры КомпоновщикМакета.

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

← На главную