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

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

При разработке в 1С мы часто сталкиваемся с необходимостью передать программно созданную таблицу значений в качестве параметра в запрос для дальнейшего помещения во временную таблицу. И довольно часто этот процесс прерывается досадной ошибкой: «Тип не может быть выбран в запросе». Давайте вместе разберем, почему возникает эта проблема, и посмотрим на практическом примере, как ее правильно диагностировать и исправлять.

Анализ практической ситуации

Рассмотрим ситуацию, с которой столкнулся разработчик. Он получает данные из веб-сервиса, помещает их в таблицу значений, а затем передает эту таблицу в запрос. Посмотрим на исходный код:


ТаблицаЗначений = Новый ТаблицаЗначений; 
ТаблицаЗначений.Колонки.Добавить("id", новый ОписаниеТипов("Строка")); 
КвалификаторыСтрокиfullName = Новый КвалификаторыСтроки(250); 

// Обратите внимание на следующую строку:
ТаблицаЗначений.Колонки.Добавить("fullName",,новый ОписаниеТипов("Строка",,,,КвалификаторыСтрокиfullName)); 

ТаблицаЗначений.Колонки.Добавить("birthday", новый ОписаниеТипов("Дата")); 
КвалификаторыСтрокиStudyStatus = Новый КвалификаторыСтроки(150); 
ТаблицаЗначений.Колонки.Добавить("StudyStatus", новый ОписаниеТипов("Строка",,,,КвалификаторыСтрокиStudyStatus)); 
ТаблицаЗначений.Колонки.Добавить("date", новый ОписаниеТипов("Дата")); 
КвалификаторыСтрокиTypeOfFundingSource = Новый КвалификаторыСтроки(350); 
ТаблицаЗначений.Колонки.Добавить("TypeOfFundingSource", новый ОписаниеТипов("Строка",,,,КвалификаторыСтрокиTypeOfFundingSource)); 

Запрос = Новый Запрос; 
Запрос.Текст = 
"ВЫБРАТЬ 
|    ДанныеУОП.id КАК id, 
|    ДанныеУОП.fullName КАК fullName, 
|    ДанныеУОП.birthday КАК birthday, 
|    ДанныеУОП.StudyStatus КАК StudyStatus, 
|    ДанныеУОП.date КАК date, 
|    ДанныеУОП.TypeOfFundingSource КАК TypeOfFundingSource 
|ПОМЕСТИТЬ ВТ_Отобранные 
|ИЗ 
|    &ДанныеУОП КАК ДанныеУОП";

При выполнении этого кода платформа выдала ошибку именно на поле fullName. Выясним причину. Если мы внимательно проанализируем строку создания колонки, то увидим синтаксическую ошибку в параметрах метода Добавить и конструктора ОписаниеТипов.

Решение: устранение синтаксической ошибки

Разберем по шагам создание колонки. Конструктор ОписаниеТипов имеет определенную сигнатуру. Обратимся к синтакс-помощнику. Если мы хотим передать квалификаторы строки, мы должны передать их в соответствующий параметр. В исходном коде разработчик написал:


ТаблицаЗначений.Колонки.Добавить("fullName",,новый ОписаниеТипов("Строка",,,,КвалификаторыСтрокиfullName));

Здесь допущено сразу две ошибки с позиционированием параметров через запятые:

  1. В методе ТаблицаЗначений.Колонки.Добавить второй параметр — это тип колонки. Разработчик поставил лишнюю запятую после имени колонки "fullName", из-за чего объект ОписаниеТипов ушел в третий параметр (заголовок колонки), а сама колонка осталась без жесткой типизации (получила произвольный тип).
  2. В конструкторе ОписаниеТипов тоже нужно быть аккуратными с количеством пропущенных параметров.

Правильный вариант кода должен выглядеть так:


КвалификаторыСтрокиfullName = Новый КвалификаторыСтроки(250);
ОписаниеТиповСтрока250 = Новый ОписаниеТипов("Строка", , КвалификаторыСтрокиfullName);
ТаблицаЗначений.Колонки.Добавить("fullName", ОписаниеТиповСтрока250);

Как только мы убрали лишние запятые и правильно передали ОписаниеТипов во второй параметр, база данных смогла определить тип колонки fullName, и ошибка исчезла.

Глубокий разбор: почему запросы так чувствительны к типам?

Давайте проанализируем ситуацию шире. Почему движок запросов 1С и СУБД вообще требуют строгой типизации всех полей для создания временных таблиц? Дело в том, что когда мы используем оператор ПОМЕСТИТЬ, платформа дает команду СУБД (например, MS SQL Server или PostgreSQL) создать реальную физическую временную таблицу в базе данных tempdb. СУБД не умеет создавать колонки с типом "что угодно". Ей нужны четкие инструкции: это число (int), это строка (varchar), это дата (datetime). Если 1С не может сопоставить тип колонки таблицы значений с типом SQL, она прерывает работу с ошибкой.

Рассмотрим другие распространенные причины, по которым возникает ошибка «Тип не может быть выбран в запросе».

1. Отсутствие типизации колонок при программном создании (Произвольный тип)

Если мы создаем колонку просто указав ее имя:


ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("НоваяКолонка");

Такая колонка по умолчанию имеет произвольный тип. Запрос не сможет с ней работать. Всегда строго типизируйте колонки перед передачей в запрос с помощью объекта ОписаниеТипов.

2. Наличие колонок с типом «Неопределено» или «Null»

Часто мы выгружаем результаты одного запроса в таблицу значений с помощью метода Выгрузить(), а затем передаем эту таблицу во второй запрос. Если в первом запросе использовалось левое соединение, и в результате образовалась колонка, полностью состоящая из значений NULL, или поле имело тип Неопределено, типизация колонки может быть потеряна.

Чтобы избежать этого, в исходном запросе необходимо использовать функцию ЕСТЬNULL или принудительно приводить тип через ВЫРАЗИТЬ:


ВЫБРАТЬ
    ЕСТЬNULL(Таблица.Количество, 0) КАК Количество,
    ВЫРАЗИТЬ(Таблица.Комментарий КАК Строка(100)) КАК Комментарий
...

3. Использование неподдерживаемых типов данных

Проанализируем типы данных, которые мы пытаемся передать. Запрос работает только с теми типами, которые можно сохранить в базе данных. Вы получите ошибку, если колонка содержит:

  1. УникальныйИдентификатор (UUID) — СУБД не понимает этот тип напрямую из ТЗ. Перед выборкой программно приведите колонку к типу Строка.
  2. Вложенные коллекции: ТаблицаЗначений, Массив, Структура.
  3. ХранилищеЗначения — двоичные данные нельзя обрабатывать в запросах таким образом.
  4. Клиентские объекты (элементы формы, элементы управления).

4. Строки неограниченной длины и индексирование

Если мы передаем колонку с типом Строка, но не задаем КвалификаторыСтроки с конкретной длиной (например, 100 или 250 символов), строка автоматически становится строкой неограниченной длины (в терминах SQL это тип text или varchar(max)).

Сама по себе такая таблица может загрузиться во временную таблицу. Но если мы попытаемся сделать по этой колонке группировку (СГРУППИРОВАТЬ ПО), использовать её в условии соединения (ЛЕВОЕ СОЕДИНЕНИЕ ... ПО Т1.Строка = Т2.Строка) или проиндексировать временную таблицу по этому полю, СУБД выдаст ошибку.

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

5. Поля составного типа с недопустимыми значениями

Если в таблице значений используется колонка составного типа, убедитесь, что все типы, входящие в состав, допустимы для запросов. Если в составной тип затесался, например, Массив наряду со СправочникСсылка, запрос завершится с ошибкой. Составные типы перед передачей должны быть строго ограничены только примитивными или ссылочными типами данных 1С.

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

Мы подробно разобрали причины возникновения ошибки выбора типа в запросе. При работе с таблицами значений, передаваемыми в параметры запроса, всегда соблюдайте следующие правила:

  1. Внимательно проверяйте синтаксис конструктора ОписаниеТипов.
  2. Жестко задавайте типы для всех добавляемых колонок.
  3. Ограничивайте длину строковых колонок, если они участвуют в связях.
  4. Избегайте передачи в запрос UUID, массивов, структур и значений NULL.

Соблюдение этих простых правил избавит вас от большинства проблем при работе с временными таблицами и запросами в платформе 1С:Предприятие!

← На главную