Почему не работает условие в запросе СКД, хотя в консоли всё выполняется корректно?

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

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

Анализ проблемы и исходного запроса

Рассмотрим ситуацию, с которой столкнулся автор темы. В отчете по остаткам готовой продукции на складах необходимо исключить из выборки определенные склады. Для этого у справочника Склады есть булевский реквизит НеУчитыватьВОтчетахПоОстаткамГП. Если флаг установлен (ИСТИНА), остатки по этому складу выводить не нужно.

Автор пытается наложить условие во внешнем пакете запроса СКД:


ВЫБРАТЬ
    ВложенныйЗапрос.Склад КАК Склад,
    ВложенныйЗапрос.БазоваяЕдиница,
    ВложенныйЗапрос.КоличествоСтрок КАК КоличествоСтрок,
    ВложенныйЗапрос.Категория,
    ВложенныйЗапрос.Вес,
    ВложенныйЗапрос.Количество КАК Количество,
    ВложенныйЗапрос.ГПРейтинг,
    ГотоваяПродукция.Ссылка КАК Ссылка,
    ВложенныйЗапрос.Склад.НеУчитыватьВОтчетахПоОстаткамГП
ИЗ
    (ВЫБРАТЬ
        ПродукцияНаСкладахОстатки.ГП КАК ГП,
        ПродукцияНаСкладахОстатки.ГП.БазоваяЕдиница КАК БазоваяЕдиница,
        КОЛИЧЕСТВО(1) КАК КоличествоСтрок,
        ПродукцияНаСкладахОстатки.ГП.ТоварнаяКатегория КАК Категория,
        СУММА(ПродукцияНаСкладахОстатки.КоличествоОстаток * ПродукцияНаСкладахОстатки.ГП.БазоваяЕдиница.ВесНетто) КАК Вес,
        СУММА(ПродукцияНаСкладахОстатки.КоличествоОстаток) КАК Количество,
        ПродукцияНаСкладахОстатки.ГП.Рейтинг КАК ГПРейтинг,
        ПродукцияНаСкладахОстатки.Склад КАК Склад
    ИЗ
        РегистрНакопления.ПродукцияНаСкладах.Остатки(&ДатаОтчета, ) КАК ПродукцияНаСкладахОстатки
    
    СГРУППИРОВАТЬ ПО
        ПродукцияНаСкладахОстатки.ГП,
        ПродукцияНаСкладахОстатки.ГП.БазоваяЕдиница,
        ПродукцияНаСкладахОстатки.ГП.ТоварнаяКатегория,
        ПродукцияНаСкладахОстатки.ГП.Рейтинг,
        ПродукцияНаСкладахОстатки.Склад) КАК ВложенныйЗапрос
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ГотоваяПродукция КАК ГотоваяПродукция
        ПО ВложенныйЗапрос.ГП = ГотоваяПродукция.Ссылка
ГДЕ
    НЕ ГотоваяПродукция.ЭтоГруппа
    И НЕ ГотоваяПродукция.ПометкаУдаления
    И НЕ ВложенныйЗапрос.Склад.НеУчитыватьВОтчетахПоОстаткамГП
 УПОРЯДОЧИТЬ ПО
    ГотоваяПродукция.Рейтинг

Несмотря на то, что конструкция И НЕ ВложенныйЗапрос.Склад.НеУчитыватьВОтчетахПоОстаткамГП написана синтаксически верно, при формировании отчета СКД продолжает выводить все склады без исключения. При этом перенос условия в параметры виртуальной таблицы или изменение написания на равенство ЛОЖЬ не дают никакого результата. В то же время, запуск этого же текста запроса в консоли отрабатывает корректно.

Выясняем причину: почему консоль «врет»?

Когда запрос ведет себя по-разному в консоли и в реальном отчете, дело почти никогда не связано с кэшем платформы или «барабашками». Проанализируем архитектуру структуры отчета. У любого отчета в 1С есть свойство Основная схема компоновки данных. Именно её разработчик открывает по умолчанию из окна конфигуратора и именно её он редактирует.

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

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


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

Суть проблемы: когда вы вносите изменения в основную схему через конструктор, вы думаете, что меняете логику отчета. Но в режиме предприятия срабатывает условие, которое подставляет совершенно другой макет из ветки «Макеты» вашего отчета. Именно поэтому ваши изменения игнорируются, а в консоли запросов (где вы запускали скопированный текст напрямую) всё работало правильно.

Пошаговый алгоритм отладки подобных ошибок

Если вы столкнулись с ситуацией, когда изменения в запросе СКД не отражаются на результате работы отчета, выполните следующие шаги для локализации проблемы:

  1. Проверьте коллекцию макетов отчета. Разверните ветку метаданных вашего отчета в конфигураторе и посмотрите на раздел Макеты. Если там содержатся другие макеты с типом «Схема компоновки данных», это первый сигнал о возможной подмене.
  2. Проанализируйте модуль объекта. Откройте модуль объекта отчета и найдите событие ПриКомпоновкеРезультата. Проверьте, переопределена ли там СтандартнаяОбработка и не происходит ли переустановка свойства СхемаКомпоновкиДанных.
  3. Изучите модуль формы отчета. Если отчет разработан под управляемые формы (особенно с использованием Библиотеки стандартных подсистем — БСП), схема может динамически подгружаться на сервере при создании формы или при изменении ключевых реквизитов (например, переключателя видов отчета). Ищите вызовы методов ПолучитьМакет на стороне сервера формы.
  4. Используйте отладчик. Поставьте точку останова в процедуре формирования отчета и вычислите выражение ЭтотОбъект.СхемаКомпоновкиДанных (или соответствующую переменную схемы в коде формы), чтобы убедиться, какая именно схема отправляется на выполнение в процессор компоновки.

Рекомендация по оптимизации исходного запроса

Помимо архитектурной ошибки с макетами, исходный запрос автора содержит серьезный изъян с точки зрения производительности СУБД. Обратим внимание на то, где наложено условие фильтрации складов:


ГДЕ
    НЕ ГотоваяПродукция.ЭтоГруппа
    И НЕ ГотоваяПродукция.ПометкаУдаления
    И НЕ ВложенныйЗапрос.Склад.НеУчитыватьВОтчетахПоОстаткамГП

Конструкция ВложенныйЗапрос.Склад.НеУчитыватьВОтчетахПоОстаткамГП во внешнем условии ГДЕ заставляет СУБД сначала прочитать абсолютно все остатки из физических таблиц регистра накопления по всем складам компании, сгруппировать их во вложенном запросе, выполнить левое соединение со справочником готовой продукции и неявное соединение со справочником складов через точку. И только после построения огромной временной таблицы в памяти СУБД начнет отсекать ненужные склады.

Для оптимизации запроса перенесем условие фильтрации непосредственно в параметры виртуальной таблицы остатков. Проанализируем правильный вариант построения запроса:


ВЫБРАТЬ
    ПродукцияНаСкладахОстатки.Склад КАК Склад,
    ПродукцияНаСкладахОстатки.ГП.БазоваяЕдиница КАК БазоваяЕдиница,
    КОЛИЧЕСТВО(1) КАК КоличествоСтрок,
    ПродукцияНаСкладахОстатки.ГП.ТоварнаяКатегория КАК Категория,
    СУММА(ПродукцияНаСкладахОстатки.КоличествоОстаток * ПродукцияНаСкладахОстатки.ГП.БазоваяЕдиница.ВесНетто) КАК Вес,
    СУММА(ПродукцияНаСкладахОстатки.КоличествоОстаток) КАК Количество,
    ПродукцияНаСкладахОстатки.ГП.Рейтинг КАК ГПРейтинг,
    ГотоваяПродукция.Ссылка КАК Ссылка
ИЗ
    РегистрНакопления.ПродукцияНаСкладах.Остатки(
        &ДатаОтчета,
        НЕ Склад.НеУчитыватьВОтчетахПоОстаткамГП
        И НЕ ГП.ЭтоГруппа
        И НЕ ГП.ПометкаУдаления
    ) КАК ПродукцияНаСкладахОстатки
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ГотоваяПродукция КАК ГотоваяПродукция
    ПО ПродукцияНаСкладахОстатки.ГП = ГотоваяПродукция.Ссылка

СГРУППИРОВАТЬ ПО
    ПродукцияНаСкладахОстатки.Склад,
    ПродукцияНаСкладахОстатки.ГП.БазоваяЕдиница,
    ПродукцияНаСкладахОстатки.ГП.ТоварнаяКатегория,
    ПродукцияНаСкладахОстатки.ГП.Рейтинг,
    ГотоваяПродукция.Ссылка

При таком подходе фильтрация по реквизиту склада НеУчитыватьВОтчетахПоОстаткамГП происходит на самом раннем этапе — СУБД считывает из индекса таблицы остатков только те записи, которые соответствуют условию. Это кардинально снижает нагрузку на сервер, уменьшает объем сканируемых данных и ускоряет формирование отчета в десятки раз на крупных базах данных.

Альтернативные подходы: варианты отчетов вместо множества схем

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

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

← На главную