Почему возникает ошибка «Неверные параметры» при создании временной таблицы из ТаблицыЗначений не первым запросом в пакете?

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

Разберем специфическую ошибку платформы 1С:Предприятие 8.3, с которой часто сталкиваются разработчики после обновления конфигураций или повышения режима совместимости. Ситуация выглядит следующим образом: в коде выполняется пакетный запрос, где во временную таблицу помещаются данные из переданного внешнего источника — объекта ТаблицаЗначений или ТабличнаяЧасть. В конструкторе запросов синтаксис распознается корректно, однако при непосредственном выполнении метода Выполнить() платформа выдает исключение вида:

Неверные параметры «ИмяТаблицы» <<?>>&ИмяТаблицы КАК ИмяТаблицы

Проанализируем причины такого поведения транслятора запросов и рассмотрим практические способы решения этой проблемы.

Причины возникновения ошибки

Выясним причину сбоя в механизмах платформы. При передаче прикладного объекта (такого как ТаблицаЗначений) в параметр запроса 1С выполняет предварительную выгрузку этих строк во временные структуры СУБД (например, в базу tempdb для MS SQL Server). В современных версиях платформы и на актуальных режимах совместимости логика оптимизатора и транслятора запросов была изменена:

  1. Ранняя подготовка внешних источников данных. Связывание параметров внешних наборов данных и генерация контекста временных таблиц происходят на начальном этапе разбора всего пакета запросов. Если конструкция выборки из внешнего источника &Параметр расположена после запросов к реальным таблицам базы данных, механизм связывания платформы в ряде сценариев не может корректно определить контекст типов переданного набора, что приводит к ошибке Неверные параметры.
  2. Смена режима совместимости конфигурации. При обновлении типовых конфигураций (например, переход на новые релизы УТ 11, КА или ERP) часто повышается версия режима совместимости конфигурации с платформой. Синтаксический анализатор на новых режимах совместимости выполняет значительно более строгую валидацию структуры пакета запросов, блокируя отложенную инициализацию внешних таблиц, которая могла ошибочно «прощаться» старыми версиями.
  3. Неявная типизация колонок источника. Ошибка связывания усиливается, если переданная ТаблицаЗначений содержит колонки с неопределенным типом (созданные без вызова конструктора ОписаниеТипов) или содержит значения объектных типов (например, СправочникОбъект вместо ссылки), которые не поддерживаются СУБД.

Способ 1. Перенос запросов создания ВТ из параметров в начало пакета

Рассмотрим самое простое и быстрое исправление. Если в рамках одного пакетного запроса создаются временные таблицы на основе переданных таблиц значений, их размещение необходимо сделать строго приоритетным. Поместим запрос с конструкцией ПОМЕСТИТЬ из внешнего набора данных самым первым элементом в пакете.

Посмотрим на пример структуры запроса, которая вызывает ошибку:


// Ошибочный порядок: сначала идет запрос к таблице базы, затем создание ВТ из параметра
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ТаблицаРаспоряжений.Документ КАК Документ,
    ТаблицаРаспоряжений.Количество КАК Количество
ПОМЕСТИТЬ ВТ_Распоряжения
ИЗ
    &ТаблицаРаспоряжений КАК ТаблицаРаспоряжений;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Распоряжения.Документ КАК Документ
ИЗ
    ВТ_Распоряжения КАК ВТ_Распоряжения
;

Теперь исправим структуру пакета, переместив инициализацию временной таблицы на первое место:


// Корректный порядок: внешняя таблица загружается первой
ВЫБРАТЬ
    ТаблицаРаспоряжений.Документ КАК Документ,
    ТаблицаРаспоряжений.Количество КАК Количество
ПОМЕСТИТЬ ВТ_Распоряжения
ИЗ
    &ТаблицаРаспоряжений КАК ТаблицаРаспоряжений;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Распоряжения.Документ КАК Документ
ИЗ
    ВТ_Распоряжения КАК ВТ_Распоряжения
;

Если требуется передать несколько разных таблиц значений, разместим их последовательно друг за другом в начале пакета:


ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Первая ИЗ &Таблица1 КАК Таблица1;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Вторая ИЗ &Таблица2 КАК Таблица2;
////////////////////////////////////////////////////////////////////////////////
// Далее следуют основные запросы пакета к реальным таблицам БД и созданным ВТ

Способ 2. Использование объекта МенеджерВременныхТаблиц

Разберем по шагам альтернативное архитектурное решение. В сложных алгоритмах или при динамической сборке текста запроса перестроение последовательности пакета может быть неудобным. В таких случаях выгрузку внешних таблиц в СУБД рекомендуется изолировать с помощью объекта МенеджерВременныхТаблиц.

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


МенеджерВТ = Новый МенеджерВременныхТаблиц;

// Шаг 1: Загрузка таблицы значений в СУБД
ЗапросЗагрузки = Новый Запрос;
ЗапросЗагрузки.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросЗагрузки.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Номенклатура КАК Номенклатура,
    |	ТЗ.Количество КАК Количество
    |ПОМЕСТИТЬ ВТ_ДанныеИсточника
    |ИЗ
    |	&ВнешняяТаблица КАК ТЗ";

ЗапросЗагрузки.УстановитьПараметр("ВнешняяТаблица", ИсходнаяТаблицаЗначений);
ЗапросЗагрузки.Выполнить();

// Шаг 2: Основной запрос, использующий созданную ВТ
ОсновнойЗапрос = Новый Запрос;
ОсновнойЗапрос.МенеджерВременныхТаблиц = МенеджерВТ;
ОсновнойЗапрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток
    |ПОМЕСТИТЬ ВТ_Остатки
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_ДанныеИсточника.Номенклатура КАК Номенклатура,
    |	ВТ_ДанныеИсточника.Количество КАК Требуется,
    |	ЕСТЬNULL(ВТ_Остатки.Остаток, 0) КАК ОстатокНаСкладе
    |ИЗ
    |	ВТ_ДанныеИсточника КАК ВТ_ДанныеИсточника
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Остатки КАК ВТ_Остатки
    |		ПО ВТ_ДанныеИсточника.Номенклатура = ВТ_Остатки.Номенклатура";

Результат = ОсновнойЗапрос.Выполнить();

// Шаг 3: Освобождение ресурсов в tempdb
МенеджерВТ.Закрыть();

Обратите внимание: одному объекту Запрос нельзя назначить более одного экземпляра МенеджерВременныхТаблиц. Однако один менеджер может хранить множество созданных таблиц, доступных между разными вызовами запросов в рамках одного сеанса.

Способ 3. Проверка корректности типов переданных данных

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

Убедимся, что все колонки таблицы типизированы перед передачей в объект Запрос:


// Формируем таблицу с явным указанием типов
ТаблицаПараметр = Новый ТаблицаЗначений;

МассивТиповСсылка = Новый Массив;
МассивТиповСсылка.Добавить(Тип("ДокументСсылка.ЗаказКлиента"));
ОписаниеТипаСсылка = Новый ОписаниеТипов(МассивТиповСсылка);

КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 3, ДопустимыйЗнак.Положительный);
ОписаниеТипаЧисло = Новый ОписаниеТипов("Число", , , КвалификаторыЧисла);

ТаблицаПараметр.Колонки.Добавить("Документ", ОписаниеТипаСсылка);
ТаблицаПараметр.Колонки.Добавить("Количество", ОписаниеТипаЧисло);

// Заполняем данными и передаем в запрос
Запрос.УстановитьПараметр("ТаблицаРаспоряжений", ТаблицаПараметр);

Важные правила работы с внешними таблицами в запросах

  1. Запрет прямого чтения без ПОМЕСТИТЬ: Платформа 1С не позволяет выполнять прямое соединение или выборку из переданной коллекции данных. Конструкция вида ВЫБРАТЬ ... ИЗ &Таблица КАК Таблица обязательно должна содержать ключевое слово ПОМЕСТИТЬ во временную таблицу.
  2. Использование массивов вместо таблиц: Если вам требуется передать список значений только из одной колонки (например, перечень ссылок номенклатуры или контрагентов), не передавайте объект ТаблицаЗначений. Гораздо производительнее использовать метод ВыгрузитьКолонку() для получения одномерного массива и передавать его в условие ГДЕ Ссылка В (&МассивСсылок), минуя создание временных таблиц в базе данных.
← На главную