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

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

При разработке механизмов интеграции, обмена данными или групповой обработки объектов метаданных часто возникает задача передачи таблицы значений во временную таблицу запроса. Если в этой таблице присутствует колонка с типом ОписаниеТипов (например, при выгрузке реквизита ТипЗначения планов видов характеристик), платформа 1С:Предприятие генерирует исключение: «Тип не может быть выбран в запросе».

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

Воспроизведение проблемы

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


ТЗ = Новый ТаблицаЗначений;
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("ОписаниеТипов"));

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

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Ссылка КАК Ссылка,
    |	ТЗ.ТипЗначения КАК ТипЗначения
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |	&ТЗ КАК ТЗ";

Запрос.УстановитьПараметр("ТЗ", ТЗ);
Запрос.Выполнить();

При выполнении метода Запрос.Выполнить() система выдает ошибку: {(3, 2)}: Тип не может быть выбран в запросе >ТЗ.ТипЗначения.

При этом разработчики часто замечают кажущийся парадокс: если выполнить прямой запрос к реальной таблице базы данных (например, к плану видов характеристик ВидыСубконтоХозрасчетные), то обращение к полю ТипЗначения и его помещение во временную таблицу отрабатывают без ошибок:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВидыСубконто.Ссылка КАК Ссылка,
    |	ВидыСубконто.ТипЗначения КАК ТипЗначения
    |ПОМЕСТИТЬ ВТ_Субконто
    |ИЗ
    |	ПланВидовХарактеристик.ВидыСубконтоХозрасчетные КАК ВидыСубконто;
    |
    |ВЫБРАТЬ
    |	ВТ.Ссылка,
    |	ВТ.ТипЗначения
    |ИЗ
    |	ВТ_Субконто КАК ВТ";

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

Выясним причину ограничения платформы

Разберем, почему один сценарий работает, а второй вызывает сбой:

  1. Трансляция таблицы значений в СУБД. Когда мы передаем объект ТаблицаЗначений в запрос в качестве параметра, платформа создает физическую временную таблицу в базе данных (например, в базе данных tempdb на MS SQL Server или во временном хранилище PostgreSQL). Для создания такой таблицы платформа должна однозначно сопоставить тип каждой колонки со скалярным типом данных СУБД (строка, число, дата, булево, UUID).
  2. Природа объекта «ОписаниеТипов». Этот объект является сложной системной структурой платформы 1С, инкапсулирующей в себе перечень доступных типов, квалификаторы строк, чисел, дат и двоичных данных. В реляционных СУБД нет эквивалентного скалярного типа данных. Генератор запросов платформы не умеет сериализовать такие объекты во временные таблицы СУБД «на лету».
  3. Хранение в таблицах метаданных. В реальных таблицах базы данных реквизит ТипЗначения хранится в специальном внутреннем бинарном формате платформы. Внутренний механизм чтения запросов умеет извлекать и интерпретировать это поле при обращении к существующим таблицам метаданных, но не поддерживает создание произвольных внешних колонок с этим типом.

Способ 1. Исключение сложного типа из временной таблицы (рекомендуемый)

В большинстве задач сопоставления объектов (например, при синхронизации планов видов характеристик между базами) сравнивать ОписаниеТипов непосредственно внутри текста запроса не требуется. Рекомендуется помещать в запрос только ключевые реквизиты объекта, а проверку типа выполнять средствами встроенного языка.

Разберем по шагам реализацию данного подхода:

  1. Формируем таблицу значений без колонки сложного типа, используя идентификаторы для сопоставления (код, наименование, уникальный идентификатор).
  2. С помощью запроса находим совпадения объектов в базе данных.
  3. Для найденных ссылок выполняем прямое сравнение объектов ОписаниеТипов на встроенном языке. Платформа штатно поддерживает оператор равенства (=) для двух объектов ОписаниеТипов, который учитывает состав типов и все квалификаторы.

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

Запрос.УстановитьПараметр("ТЗ", ТЗ_БезСложныхКолонок);
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    ИсходноеОписание = ПолучитьИсходноеОписание(Выборка.Код);
    
    // Прямое сравнение на встроенном языке
    Если Выборка.ТипЗначенияБазы = ИсходноеОписание Тогда
        // Типы полностью совпадают
    Иначе
        // Требуется обновление типа значения
    КонецЕсли;
КонецЦикла;

Способ 2. Сериализация в XDTO и вычисление хэша

Если сопоставление типов обязательно должно выполняться именно средствами СУБД (через оператор связи ПО в запросе), объект ОписаниеТипов можно преобразовать в скалярное строковое значение. Самым надежным вариантом является получение XML-строки с последующим расчетом контрольной суммы.

Рассмотрим реализацию с использованием объектов СериализаторXDTO и ХешированиеДанных:


// Функция получения строкового хэша описания типов
Функция ПолучитьХэшОписанияТипов(ТекущееОписаниеТипов)
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    
    СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ТекущееОписаниеТипов);
    СтрокаXML = ЗаписьXML.Закрыть();
    
    Хеширование = Новый ХешированиеДанных(ХешФункция.MD5);
    Хеширование.Добавить(СтрокаXML);
    
    Возврат Строка(Хеширование.ХешСумма);
КонецФункции

Теперь в таблицу значений вместо объекта добавляется строковая колонка фиксированной длины (32 символа для MD5):


ТЗ.Колонки.Добавить("ХэшТипа", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(32)));

Для Каждого Стр Из ТЗ Цикл
    Стр.ХэшТипа = ПолучитьХэшОписанияТипов(Стр.ИсходноеОписание);
КонецЦикла;

Такая колонка гарантированно передается во временную таблицу без ошибок, позволяя выполнять быстрые соединения и фильтрацию на уровне СУБД.

Способ 3. Нормализация состава типов во вспомогательную таблицу

Если задача требует анализа каждого конкретного типа, входящего в составное описание (например, проверить наличие определенного справочника), коллекцию типов можно развернуть в плоскую вспомогательную таблицу вида «один-ко-многим»:


ТЗ_Типы = Новый ТаблицаЗначений;
ТЗ_Типы.Колонки.Добавить("ИдентификаторОбъекта", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(36)));
ТЗ_Типы.Колонки.Добавить("ИмяТипа", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));

Для Каждого Элемент ИсходныхДанных Из СписокЭлементов Цикл
    Для Каждого КонкретныйТип Из Элемент.ОписаниеТипов.Типы() Цикл
        НоваяСтрока = ТЗ_Типы.Добавить();
        НоваяСтрока.ИдентификаторОбъекта = Элемент.Идентификатор;
        НоваяСтрока.ИмяТипа = XMLСтрока(КонкретныйТип);
    КонецЦикла;
КонецЦикла;

Таблица со строковыми именами типов легко помещается во временную таблицу запроса и позволяет строить гибкие условия отбора с группировками и соединениями.

Опасность использования «ЗначениеВСтрокуВнутр»

Иногда на практике пытаются сериализовать ОписаниеТипов через функцию ЗначениеВСтрокуВнутр(). Важно понимать риски этого подхода: функция сохраняет внутренние системные идентификаторы конфигурации конкретной базы данных. При передаче таких строк между разными базами (даже с идентичной конфигурацией) функция ЗначениеИзСтрокиВнутр() может восстановить некорректные или «битые» типы метаданных из-за несовпадения внутренних идентификаторов. Для надежного переноса и сравнения типов следует использовать механизмы СериализаторXDTO или сопоставление по строковым именам метаданных.

← На главную