Ошибка «Тип не может быть выбран в запросе» в 1С 8.3: типизация ТаблицыЗначений
Ошибка {(X, Y)}: Тип не может быть выбран в запросе при помещении ТаблицаЗначений во временную таблицу (ВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ) возникает по трём причинам: у колонки не задано ОписаниеТипов, для строковой колонки не указана фиксированная ненулевая длина в КвалификаторыСтроки(Длина) либо в колонке лежит прикладной тип, который СУБД не может разместить во временной таблице (Структура, Соответствие, Массив, ХранилищеЗначения, ДокументОбъект).
Чтобы запрос выполнился без ошибок, создавайте колонки через ТЗ.Колонки.Добавить("Имя", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150))), для чисел задавайте Новый КвалификаторыЧисла(15, 2), а перед вызовом Запрос.Выполнить() инициализируйте Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц.
Почему возникает ошибка «Тип не может быть выбран в запросе»
При выполнении пакета с конструкцией ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ платформа 1С:Предприятие 8.3 транслирует переданную в параметр коллекцию в реальную временную таблицу СУБД (например, #tt1 в MS SQL Server или временную таблицу в PostgreSQL). Для команды CREATE TABLE серверу баз данных нужны точные SQL-типы каждого выбираемого поля: NUMERIC(15, 2), NVARCHAR(100), BINARY(16) для ссылок или DATETIME.
Если разработчик создал колонку вызовом ТЗ.Колонки.Добавить("Номенклатура") без второго параметра или получил ТаблицаЗначений из внешней обработки без типизации, свойство Колонка.ТипЗначения содержит пустое ОписаниеТипов (произвольный тип). Платформа не знает, какую структуру колонок создавать в СУБД, и на строке Запрос.Выполнить() выбрасывает исключение:
Ошибка при вызове метода контекста (Выполнить):
{(2, 2)}: Тип не может быть выбран в запросе
<<?>>ТЗ.Артикул КАК Артикул,
Координаты {(X, Y)} в тексте исключения всегда указывают на номер строки и позицию поля в секции ВЫБРАТЬ, тип которого не удалось отобразить в тип поля временной таблицы.
// ПРОБЛЕМНЫЙ КОД: вызовет исключение на Запрос.Выполнить()
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Артикул"); // Тип не задан (Произвольный)
ТЗ.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка")); // Длина = 0!
СтрокаТЗ = ТЗ.Добавить();
СтрокаТЗ.Артикул = "АРТ-00125";
СтрокаТЗ.Комментарий = "Поставка по договору";
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Артикул КАК Артикул,
| ТЗ.Комментарий КАК Комментарий
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТЗ КАК ТЗ";
Запрос.УстановитьПараметр("ТЗ", ТЗ);
Запрос.Выполнить(); // Ошибка: Тип не может быть выбран в запросе
Строка — идентификатор колонки для обращения в запросеОписаниеТипов — без него создаётся произвольный тип, запрещённый в ПОМЕСТИТЬ ВТСтрока — заголовок колонкиЧисло — ширина в символахДва жёстких требования к колонкам ТаблицыЗначений
Чтобы таблица значений без ошибок прошла через ПОМЕСТИТЬ ВТ, каждая выбираемая в запросе колонка должна одновременно выполнять два условия платформы 1С 8.3:
- Задан конкретный
ОписаниеТипов. ВКолонка.ТипЗначениядолжен быть указан один примитивный тип, конкретная ссылка (например,СправочникСсылка.Номенклатура) или допустимый составной тип из ссылок и примитивов. Значение по умолчанию (пустой конструкторНовый ОписаниеТипов()) не допускается. - Для типа
Строказадана ненулевая фиксированная длина вКвалификаторыСтроки. Когда вы пишетеНовый ОписаниеТипов("Строка")без квалификаторов, платформа создаёт описание строки сКвалификаторыСтроки.Длина = 0(неограниченная длина, аналогMEMO/NVARCHAR(MAX)/TEXT). В языке запросов 1С поля неограниченной длины нельзя помещать во временные таблицы, группировать и индексировать, поэтому на этапеПОМЕСТИТЬ ВТвозникает та же самая ошибкаТип не может быть выбран в запросе.
ТЗ = Новый ТаблицаЗначений;
// 1. Строка: ОБЯЗАТЕЛЬНО указываем ненулевую длину в КвалификаторыСтроки
КвСтроки100 = Новый КвалификаторыСтроки(100, ДопустимаяДлина.Переменная);
ТЗ.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , КвСтроки100));
// 2. Число: указываем разрядность и точность в КвалификаторыЧисла
КвЧисла = Новый КвалификаторыЧисла(15, 3, ДопустимыйЗнак.Любой);
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", КвЧисла));
// 3. Дата: указываем состав даты (Дата, Время или ДатаВремя)
КвДаты = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя);
ТЗ.Колонки.Добавить("ДатаПоставки", Новый ОписаниеТипов("Дата", , , КвДаты));
// 4. Булево и конкретная ссылка: квалификаторы не требуются
ТЗ.Колонки.Добавить("ЭтоУслуга", Новый ОписаниеТипов("Булево"));
ТЗ.Колонки.Добавить("Номенклатура",
Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
// 5. Составной тип документа (несколько ссылочных типов)
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
МассивТипов.Добавить(Тип("ДокументСсылка.ПоступлениеТоваровУслуг"));
ТЗ.Колонки.Добавить("ДокументОснование", Новый ОписаниеТипов(МассивТипов));
Строка с именами типов через запятую или Массив из ТипКвалификаторыЧисла(Разрядность, Точность, Знак) — 2-й параметрКвалификаторыСтроки(Длина, ДопустимаяДлина) — 3-й параметр, Длина > 0КвалификаторыДаты(ЧастиДаты) — 4-й параметрВажно учитывать, что если у колонки ТаблицаЗначений длина строки равна 0 (Новый ОписаниеТипов("Строка")) или тип колонки произвольный, то обёртка ВЫРАЗИТЬ(ТЗ.Комментарий КАК СТРОКА(250)) при чтении ИЗ &ТЗ ошибку не устранит — платформа проверяет типы колонок внешнего источника до вычисления выражений выборки. Оператор ВЫРАЗИТЬ(... КАК СТРОКА(250)) нужно применять на шаг раньше — в том запросе к базе данных, результат которого выгружается в ТЗ через Выгрузить(), либо исправлять Колонка.ТипЗначения в коде 1С перед передачей параметра.
ОписаниеТипов с нужными квалификаторами и выполнит пакетный запрос к вашей базе. Посмотреть возможности и подключить инструмент можно на странице тарифов ИИ Консоли запросов 1С.
Какие типы вообще нельзя выбирать в запросе из ТаблицыЗначений
Даже если вы аккуратно создали Новый ОписаниеТипов(...) для каждой колонки, язык запросов 1С 8.3 поддерживает только те типы данных, которые имеют прямое представление в таблицах базы данных. Прикладные коллекции, мутабельные объекты и бинарные контейнеры выбрать в секции ВЫБРАТЬ из источника &ТЗ нельзя.
| Категория типа в колонке ТЗ | Примеры типов 1С 8.3 | Допустимость в ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ |
Как правильно передать в запрос |
|---|---|---|---|
| Примитивные типы (с квалификаторами) | Число, Строка(N) (N > 0), Дата, Булево |
Полностью поддерживаются | Задать КвалификаторыЧисла и КвалификаторыСтроки(N) при создании колонки |
| Ссылочные типы и перечисления | СправочникСсылка.*, ДокументСсылка.*, ПеречислениеСсылка.*, ПланВидовХарактеристикСсылка.* |
Полностью поддерживаются | Указать точное имя типа метаданных в ОписаниеТипов |
| Системные идентификаторы | УникальныйИдентификатор, МоментВремени, Граница |
В ВЫБРАТЬ не выбираются (вызовут ошибку типа) |
Заранее преобразовывать в ссылку через Справочники.Номенклатура.ПолучитьСсылку(УИД) или передавать в ТЗ как Строка(36) (а в запросе к БД получать функцией УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Ссылка)) |
| Прикладные объекты (в памяти) | СправочникОбъект.*, ДокументОбъект.*, ОтчетОбъект.*, ОбработкаОбъект.* |
Запрещены | Заменить в колонке объект на его ссылку: СтрокаТЗ.Документ = ДокОбъект.Ссылка |
| Коллекции и контейнеры | Структура, Соответствие, Массив, СписокЗначений, ТаблицаЗначений, ХранилищеЗначения, ДвоичныеДанные |
Запрещены | Удалить служебную колонку из ТЗ или не включать её в список полей ВЫБРАТЬ (отказаться от ВЫБРАТЬ *) |
| Описатели типов | Тип, ОписаниеТипов, СтандартнаяДатаНачала, СтандартныйПериод |
Запрещены в колонках источника ВТ | Передавать отдельным параметром запроса (&НужныйТип) или строковым именем |
Обратите внимание на важный нюанс: если в вашей ТаблицаЗначений 10 колонок, и в 9-й колонке лежит Структура или ХранилищеЗначения для клиентской логики формы, вам не обязательно удалять эту колонку из ТЗ. Достаточно в тексте запроса явно перечислить только нужные типизированные колонки (ВЫБРАТЬ ТЗ.Номенклатура, ТЗ.Количество ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ) вместо использования звёздочки ВЫБРАТЬ *.
Ошибка при передаче Массива во временную таблицу
Исключение Тип не может быть выбран в запросе возникает не только с ТаблицаЗначений, но и при попытке поместить во временную таблицу обычный Массив конструкцией (при этом СписокЗначений передавать в секцию ИЗ &Список нельзя вообще — он допустим только в условии В (&Список)):
ВЫБРАТЬ
Список.Значение КАК Номенклатура
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
&МассивНоменклатуры КАК Список
В отличие от ТаблицаЗначений, у объекта Массив в 1С 8.3 нет описания типов колонок. Платформа определяет тип единственного поля Значение по фактическим элементам массива. Ошибка появляется в трёх случаях:
- Передан пустой массив (
Массив.Количество() = 0). Внутри нет ни одного элемента, поэтому платформа не может вывести тип поляЗначениеи отказывается создавать временную таблицу. - В массиве лежат строки или числа. У примитивных элементов массива нет фиксированных квалификаторов длины и разрядности, поэтому 1С трактует их как
Строка(0)/Число(0)неограниченной длины. - В массиве есть хотя бы одно значение
НеопределеноилиNull.
Поскольку у пустого массива и массива строк нет квалификаторов типа на уровне источника, конструкция ВЫРАЗИТЬ(Список.Значение КАК ...) при чтении ИЗ &МассивНоменклатуры от падения не защитит. Надёжное решение — вместо Массива передавать во временную таблицу одноколоночную типизированную ТаблицаЗначений с явно заданным ОписаниеТипов.
Готовая универсальная функция подготовки и проверки ТаблицыЗначений
На практике в запрос часто передают ТаблицаЗначений, которая пришла из результата выгрузки табличной части, чтения файла Excel/JSON или метода другой подсистемы. Ниже приведена готовая серверная функция, которая проверяет каждую колонку ТаблицаЗначений, автоматически выводит ОписаниеТипов по фактическим данным (если тип не был задан или для строки установлена нулевая длина) и гарантированно подготавливает таблицу к передаче в Запрос.УстановитьПараметр("ТЗ", ТЗ) и МенеджерВременныхТаблиц.
// Проверяет и при необходимости типизирует колонки ТаблицыЗначений
// перед передачей в пакетный запрос с ПОМЕСТИТЬ ВТ.
Функция ПодготовитьТЗДляЗапроса(ИсходнаяТЗ, ДлинаСтрокиПоУмолчанию = 250) Экспорт
ТЗ = ИсходнаяТЗ.Скопировать();
КвСтроки = Новый КвалификаторыСтроки(ДлинаСтрокиПоУмолчанию, ДопустимаяДлина.Переменная);
КвЧисла = Новый КвалификаторыЧисла(15, 3, ДопустимыйЗнак.Любой);
КвДаты = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя);
Для Каждого Колонка Из ТЗ.Колонки Цикл
Описание = Колонка.ТипЗначения;
ТипыКолонки = Описание.Типы();
// Случай 1: тип колонки вообще не задан (Произвольный)
Если ТипыКолонки.Количество() = 0 Тогда
МассивНайденныхТипов = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
Знач = СтрокаТЗ[Колонка.Имя];
Если Знач <> Неопределено И Знач <> Null Тогда
ТипЗнач = ТипЗнч(Знач);
Если МассивНайденныхТипов.Найти(ТипЗнач) = Неопределено Тогда
МассивНайденныхТипов.Добавить(ТипЗнач);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если МассивНайденныхТипов.Количество() = 0 Тогда
// Если колонка полностью пустая — задаём Строка(Длина), чтобы запрос не упал
МассивНайденныхТипов.Добавить(Тип("Строка"));
КонецЕсли;
Колонка.ТипЗначения = Новый ОписаниеТипов(
МассивНайденныхТипов, КвЧисла, КвСтроки, КвДаты);
// Случай 2: тип задан, но у Строки Длина = 0 или у Числа Разрядность = 0
Иначе
НовыеКвЧисла = ?(Описание.СодержитТип(Тип("Число"))
И Описание.КвалификаторыЧисла.Разрядность = 0, КвЧисла, Неопределено);
НовыеКвСтроки = ?(Описание.СодержитТип(Тип("Строка"))
И Описание.КвалификаторыСтроки.Длина = 0, КвСтроки, Неопределено);
Если НовыеКвЧисла <> Неопределено Или НовыеКвСтроки <> Неопределено Тогда
Колонка.ТипЗначения = Новый ОписаниеТипов(
Описание, , , НовыеКвЧисла, НовыеКвСтроки);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат ТЗ;
КонецФункции
ОписаниеТипов — базовое описание колонкиМассив или Строка добавляемых типовМассив или Строка исключаемых типовПример вызова подготовленной таблицы вместе с МенеджерВременныхТаблиц:
ТипизированнаяТЗ = ПодготовитьТЗДляЗапроса(ВходящаяТЗ, 150);
МВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МВТ;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Номенклатура КАК Номенклатура,
| ТЗ.Артикул КАК Артикул,
| ТЗ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| &ТЗ КАК ТЗ
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|ВЫБРАТЬ
| ВТ.Номенклатура КАК Номенклатура,
| ВТ.Количество КАК КоличествоПоЗаявке,
| ЕСТЬNULL(Остатки.ВНаличииОстаток, 0) КАК ОстатокНаСкладе
|ИЗ
| ВТ_Товары КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
| ПО ВТ.Номенклатура = Остатки.Номенклатура";
Запрос.УстановитьПараметр("ТЗ", ТипизированнаяТЗ);
Выборка = Запрос.Выполнить().Выбрать();
Типизация при Выгрузить() из результата запроса и реквизита формы
Частый вопрос разработчиков: нужно ли вручную типизировать ТаблицаЗначений, если она получена методом РезультатЗапроса.Выгрузить() или методом РеквизитФормыВЗначение("Товары").Выгрузить()?
- После
РезультатЗапроса.Выгрузить(): все колонки, полученные из полей базы данных (реквизиты справочников, документов, измерения и ресурсы регистров), уже имеют строгиеОписаниеТиповс ненулевыми квалификаторами. Однако если в первом запросе было вычисляемое строковое выражение (например, конкатенацияПоле1 + " / " + Поле2безВЫРАЗИТЬ(... КАК СТРОКА(100))) или поле со значениемНеопределено, то в выгруженной ТЗ эта колонка получит неограниченную длину строки либо произвольный тип, и при передаче во второй запрос упадёт с ошибкой. - После
ТабличнаяЧасть.Выгрузить()илиДанныеФормыКоллекция.Выгрузить(): колонки, соответствующие реквизитам табличной части метаданных, сохраняют свои типы. Но если на управляемой форме в таблицу были программно добавлены дочерние колонки без указания квалификаторов строки, перед передачей в запрос их нужно либо исключить из спискаВЫБРАТЬ, либо пропустить черезПодготовитьТЗДляЗапроса().
Частые ошибки при передаче ТаблицыЗначений в запрос
- Использование
Новый ОписаниеТипов("Строка")без третьего параметра. Самая распространённая ловушка: разработчик уверен, что типизировал колонку, но безНовый КвалификаторыСтроки(Длина)длина строки равна0, что запрещено для временных таблиц запроса. - Перепутан порядок параметров в конструкторе
ОписаниеТипов. В платформе 1С 8.3 вторым параметром идутКвалификаторыЧисла, аКвалификаторыСтроки— третьим. Если забыть лишнюю запятую и написатьНовый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(50)), платформа выдаст ошибку несоответствия типов параметров конструктора. - Использование
ВЫБРАТЬ *из таблицы со служебными колонками. Если вТаблицаЗначенийесть хотя бы одна нетипизированная колонка или колонка соСтруктура, запросВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &ТЗупадёт. Всегда явно перечисляйте только те колонкиТЗ, которые участвуют в запросе. - Передача колонки с типом
УникальныйИдентификатор. Несмотря на то чтоУникальныйИдентификатор— системный тип платформы, язык запросов 1С не умеет выбирать его в секцииВЫБРАТЬ. Передавайте UUID в ТЗ какСтрока(36)или сразу передавайтеСправочникСсылка. - Передача
ДокументОбъектилиСправочникОбъектвместо ссылки. Если при заполнении строки ТЗ присвоитьСтрокаТЗ.Документ = ДокОбъектвместоДокОбъект.Ссылка, при попытке выбрать поле в запросе возникнет ошибкаТип не может быть выбран в запросе. - Забыли
МенеджерВременныхТаблицпри раздельном выполнении запросов. Если первый запрос выполняет толькоВЫБРАТЬ ... ПОМЕСТИТЬ ВТ ИЗ &ТЗбез последующего пакета, а свойствоЗапрос.МенеджерВременныхТаблицне заполнено, платформа выбросит ошибкуНе установлен менеджер временных таблиц.
Частые вопросы по ошибке «Тип не может быть выбран в запросе»
Почему ошибка «Тип не может быть выбран в запросе» возникает, даже если колонка типизирована как Строка?
Потому что вызов Новый ОписаниеТипов("Строка") без квалификаторов создаёт строку неограниченной длины (Длина = 0). СУБД не может создать колонку временной таблицы без фиксированного предела длины, поэтому нужно передать третий параметр: Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)).
Можно ли исправить ошибку прямо в тексте запроса через оператор ВЫРАЗИТЬ без изменения кода 1С?
Если таблица значений уже передана в секцию ИЗ &ТЗ с нетипизированной колонкой или строкой неограниченной длины (Длина = 0), оператор ВЫРАЗИТЬ(ТЗ.Поле КАК СТРОКА(100)) в этом же запросе не поможет — платформа проверяет типы колонок внешнего источника до выполнения выражений. Оператор ВЫРАЗИТЬ помогает на этапе формирования самой ТЗ запросом к базе данных (например, ВЫРАЗИТЬ(Поле1 + Поле2 КАК СТРОКА(150)) перед Выгрузить()), а для готовой ТЗ необходимо задать ОписаниеТипов колонки в коде 1С.
Как передать в запрос ТаблицуЗначений, если она пустая (0 строк)?
Пустая ТаблицаЗначений передаётся во временную таблицу без ошибок, если у всех её выбираемых колонок заранее задан корректный ОписаниеТипов с ненулевыми квалификаторами строк и чисел. Платформа создаст пустую временную таблицу с нужной структурой полей, и последующие соединения в пакете запросов отработают штатно.