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