Разработчики 1С часто сталкиваются с ситуацией, когда один и тот же запрос выдает разные результаты в «Консоли запросов» и в отчете на базе СКД. Это происходит из-за того, что генератор запросов СКД пытается быть «слишком умным»: он анализирует итоговые настройки отчета и выбрасывает из временных таблиц те поля, которые не выбраны пользователем, не участвуют в отборах или группировках. Однако часто эти поля критически важны для корректной группировки промежуточных данных. Разберем подробно, почему это происходит и как «заставить» СКД выполнять запрос именно так, как мы его написали.
Рассмотрим классический пример: нам нужно подсчитать количество документов поступления, в которых встречается определенная номенклатура. В первом запросе (временная таблица) мы выбираем Номенклатура и Ссылка, чтобы «свернуть» дубли строк внутри каждого документа. Во втором запросе мы считаем количество различных ссылок по номенклатуре.
Если в настройках отчета пользователь выводит только Номенклатура и Количество, СКД видит, что поле Ссылка во временной таблице «якобы» не используется в конечном результате. Оптимизатор удаляет Ссылка из первой ВТ. В итоге группировка в первой таблице происходит только по номенклатуре, данные искажаются, и итоговый результат становится неверным. Давайте проанализируем ситуацию на примере кода:
// Временная таблица Товары
ВЫБРАТЬ
ПоступлениеТоваровУслугТовары.Номенклатура КАК Номенклатура,
ПоступлениеТоваровУслугТовары.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Товары
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
СГРУППИРОВАТЬ ПО
ПоступлениеТоваровУслугТовары.Номенклатура,
ПоступлениеТоваровУслугТовары.Ссылка;
// Итоговый запрос
ВЫБРАТЬ
ВТ_Товары.Номенклатура,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Товары.Ссылка) КАК КоличествоДокументов
ИЗ
ВТ_Товары КАК ВТ_Товары
СГРУППИРОВАТЬ ПО
ВТ_Товары.Номенклатура
Если СКД «выкинет» Ссылка из первого запроса, группировка превратится в СГРУППИРОВАТЬ ПО Номенклатура, что полностью уничтожит смысл задачи.
Самый простой и «кошерный» способ — это настройка ролей в наборе данных СКД. На закладке Набор данных для поля, которое должно участвовать в группировке (в нашем примере это Ссылка), нужно нажать на колонку Роль и установить флаг Обязательное.
Разберем по шагам, что это дает:
Интересный способ «обмануть» оптимизатор — использование оператора ВЫРАЗИТЬ. Когда СКД видит конструкцию приведения типа, она часто перестает считать это поле «лишним» для оптимизации, так как логика преобразования типа может быть важна для СУБД.
Рассмотрим, как это выглядит в запросе:
ВЫБРАТЬ
ВЫРАЗИТЬ(Товары.Ссылка КАК Документ.ПоступлениеТоваровУслуг) КАК Ссылка,
Товары.Номенклатура
ПОМЕСТИТЬ ВТ_Товары
...
С точки зрения логики это действие избыточно (поле и так имеет этот тип), но для СКД это служит сигналом к сохранению поля в структуре временной таблицы.
Если поле нужно «протащить» через временные таблицы, но вы не хотите возиться с ролями в интерфейсе СКД, можно добавить в финальный запрос или в промежуточный запрос фиктивное условие. Выясним причину эффективности этого метода: СКД не может удалить поле из выборки, если по нему наложено условие в секции ГДЕ.
Пример реализации:
ВЫБРАТЬ
ВТ_Товары.Номенклатура,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Товары.Ссылка) КАК Количество
ИЗ
ВТ_Товары КАК ВТ_Товары
ГДЕ
ВТ_Товары.Ссылка = ВТ_Товары.Ссылка // Фиктивное условие
СГРУППИРОВАТЬ ПО
ВТ_Товары.Номенклатура
Такое условие всегда истинно, но оно жестко связывает поле Ссылка с запросом, не давая оптимизатору его исключить.
Это радикальный, но самый надежный метод управления запросом. Когда флаг Автозаполнение в наборе данных включен, СКД сама решает, какие поля и условия добавлять. Если его снять, вы берете управление на себя.
При отключенном автозаполнении мы должны использовать расширения языка запросов (фигурные скобки):
ВЫБРАТЬ указываем поля, доступные пользователю: {ВЫБРАТЬ Номенклатура.*, Ссылка.*}.ГДЕ прописываем возможные отборы: {ГДЕ Ссылка.*}.Еще один вариант, который часто применяется опытными разработчиками — назначение критичного поля Ресурсом. Посмотрим на алгоритм:
Ссылка в список ресурсов на соответствующей вкладке СКД.Максимум(Ссылка) или Количество(Различные Ссылка).Проанализировав все способы, можно сделать вывод: не существует единого «идеального» решения, всё зависит от сложности вашего запроса. Если запрос простой — достаточно поставить флаг Обязательное в ролях поля. Если вы строите сложную архитектуру с десятком временных таблиц, лучше рассмотреть вариант с отключением автозаполнения или использованием фиктивных условий.
Помните, что оптимизация СКД — это полезный механизм, призванный ускорить работу отчетов, но в задачах, где важна строгая последовательность группировок (например, расчет остатков по сложным условиям или вычисление уникальных вхождений), этот механизм требует ручного контроля со стороны программиста 1С.