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