Как исправить ошибку «Несоответствие типов (Параметр номер 1)» при левом соединении в запросе СКД?

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

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

«Ошибка получения данных по причине: Ошибка создания набора данных "НаборДанных" по причине... Ошибка обработки представления "РегистрСведений.КурсыВалют.СрезПоследних:Несоответствие типов (Параметр номер "1")"»

При этом в тексте ошибки фигурирует неявный параметр, например, ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&П3), хотя в исходном коде запроса никакого параметра &П3 разработчик не создавал.

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

---

Выясняем причину: откуда берется параметр &П3 и почему возникает конфликт типов?

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

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

Конфликт типов (ошибка «Несоответствие типов») возникает по двум основным причинам:

  1. Несоответствие типов «СтандартныйПериод» и «Дата». Очень часто на форме отчета или в его настройках используется стандартный параметр «Период» с типом данных СтандартныйПериод (который содержит дату начала и дату окончания). Виртуальная таблица СрезПоследних в качестве первого параметра строго ожидает тип Дата, МоментВремени или Граница. СКД при автозаполнении пытается неявно передать объект типа СтандартныйПериод в первый параметр среза последних, что приводит к краху СУБД из-за несовместимости типов.
  2. Специфика левого соединения. При левом соединении виртуальных таблиц компоновщик СКД пытается сопоставить временные отметки данных. Если левая таблица возвращает период, который может принимать значение NULL (что естественно для левого соединения), или тип периода определен неоднозначно, система компоновки некорректно трансформирует параметры при генерации итогового SQL-запроса.

---

Способ 1. Явное указание параметров в фигурных скобках {} (Быстрое решение)

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

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


ВЫБРАТЬ
    ДенежныеСредстваЗаймыОстаткиИОбороты.Валюта КАК Валюта,
    ДенежныеСредстваЗаймыОстаткиИОбороты.СуммаОборот КАК СуммаОборот,
    КурсыВалютСрезПоследних.Курс КАК Курс
ИЗ
    РегистрНакопления.ДенежныеСредстваЗаймы.ОстаткиИОбороты(, , Авто, , ) КАК ДенежныеСредстваЗаймыОстаткиИОбороты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних({&КонецПериода}, ) КАК КурсыВалютСрезПоследних
    ПО ДенежныеСредстваЗаймыОстаткиИОбороты.Валюта = КурсыВалютСрезПоследних.Валюта

В данном случае мы явно указали параметр {&КонецПериода} внутри круглых скобок таблицы СрезПоследних. Это запрещает СКД генерировать собственный параметр &П3 для этой таблицы.

---

Способ 2. Преобразование «Стандартного периода» в Дату на уровне параметров СКД

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

Выполним настройку по шагам:

  1. Откроем схему компоновки данных и перейдем на вкладку «Параметры».
  2. Найдем стандартный параметр (например, ПериодОтчета с типом СтандартныйПериод), который видит пользователь.
  3. Создадим новый параметр с именем КонецПериода и установим для него тип данных Дата.
  4. Для нового параметра КонецПериода установим флаг «Ограничение доступности». Это необходимо, чтобы скрыть его от пользователя и не загромождать интерфейс настроек.
  5. В поле «Выражение» для параметра КонецПериода пропишем формулу: &ПериодОтчета.ДатаОкончания.
  6. В тексте нашего запроса укажем этот параметр явно: РегистрСведений.КурсыВалют.СрезПоследних(&КонецПериода, ).

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

---

Способ 3. Связи наборов данных в СКД (Архитектурно правильный подход)

Прямое левое соединение виртуальной таблицы остатков или оборотов с виртуальной таблицей СрезПоследних в одном запросе считается серьезным антипаттерном в мире разработки 1С. Такие запросы приводят к созданию крайне неоптимальных планов запросов в СУБД (особенно MS SQL и PostgreSQL), что вызывает зависание отчетов на больших базах данных.

Для СКД существует штатный, высокопроизводительный и архитектурно правильный способ получения среза последних (например, курса валюты) на каждую дату проведения операции — **использование связей наборов данных**. Рассмотрим, как это реализовать по шагам.

Шаг 1. Создание первого набора данных

Создадим Набор данных - запрос с именем НаборДанныхИсточники. Этот запрос будет выбирать только транзакции из регистра накопления:


ВЫБРАТЬ
    ДенежныеСредстваЗаймыОстаткиИОбороты.Период КАК Период,
    ДенежныеСредстваЗаймыОстаткиИОбороты.Валюта КАК Валюта,
    ДенежныеСредстваЗаймыОстаткиИОбороты.СуммаОборот КАК СуммаОборот
ИЗ
    РегистрНакопления.ДенежныеСредстваЗаймы.ОстаткиИОбороты(, , День, , ) КАК ДенежныеСредстваЗаймыОстаткиИОбороты

Шаг 2. Создание второго набора данных

Создадим Набор данных - запрос с именем НаборДанныхКурсы. Этот запрос будет получать курс валюты на конкретную дату:


ВЫБРАТЬ
    КурсыВалютСрезПоследних.Валюта КАК Валюта,
    КурсыВалютСрезПоследних.Курс КАК Курс
ИЗ
    РегистрСведений.КурсыВалют.СрезПоследних(&ДатаСреза, Валюта = &ВалютаДляФильтра) КАК КурсыВалютСрезПоследних

Обратите внимание: мы жестко ограничили выборку параметрами &ДатаСреза и &ВалютаДляФильтра. Это критически важно для производительности.

Шаг 3. Настройка связей наборов данных

Перейдем на вкладку «Связи наборов данных» и добавим новую строку:

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

---

Способ 4. Соединение с реальной физической таблицей регистра

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

Проанализируем пример такого запроса:


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

Хотя данный запрос выглядит сложнее из-за вложенного подзапроса, он абсолютно прозрачен для компоновщика СКД. В нем отсутствуют скрытые виртуальные параметры, а СУБД с легкостью строит оптимальный план выполнения запроса, используя кластеризованные индексы по полям Период и Валюта.

---

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

Мы выяснили, что ошибка «Несоответствие типов» в СКД при левом соединении со срезом последних возникает из-за механизмов автозаполнения параметров. Для быстрого исправления достаточно использовать фигурные скобки {&Параметр} для жесткого контроля со стороны разработчика. Однако для построения надежных и быстродействующих систем автоматизации мы рекомендуем использовать разделение наборов данных и связывание их на уровне платформы СКД.

← На главную