При разработке сложных отчетов, механизмов выгрузки данных или интеграционных решений программисты 1С часто сталкиваются с необходимостью динамической работы со свойствами и типами данных. Типичный сценарий: создание универсального запроса, который должен вернуть не только сами данные, но и описание их типов (объект ОписаниеТипов) для последующей обработки (например, при выгрузке номенклатуры с дополнительными реквизитами во внешнюю систему или на сайт).
Однако попытка установить параметр запроса с типом ОписаниеТипов неизбежно приводит к возникновению системной ошибки "Неверные параметры запроса". Проанализируем, почему возникает эта ошибка, как устроена работа с типами на уровне базы данных и какие существуют надежные практические способы обхода этого ограничения платформы.
Чтобы понять природу ошибки, разберем разницу между объектом Тип и объектом ОписаниеТипов, а также принципы взаимодействия платформы 1С:Предприятие с системами управления базами данных (СУБД).
Объект Тип в 1С — это простой скалярный идентификатор конкретного класса метаданных. В базе данных он сопоставляется с числовым кодом или уникальным идентификатором таблицы метаданных. Платформа без проблем транслирует его в SQL-запрос, поэтому мы можем использовать конструкции вида ТИПЗНАЧЕНИЯ(Поле) = &ПараметрТип.
В отличие от него, ОписаниеТипов — это сложный системный объект объектной модели встроенного языка 1С. Он представляет собой не просто один тип, а целое подмножество (массив) допустимых типов данных, дополненное квалификаторами (длина строки, допустимый знак числа, разрядность даты и т.д.).
В реляционных базах данных (будь то MS SQL Server, PostgreSQL или Oracle) нет прямого аналога объекту ОписаниеТипов. Когда мы выбираем стандартный реквизит ТипЗначения из Плана видов характеристик (ПВХ), СУБД на самом деле возвращает обычную бинарную строку (поле типа VARBINARY или BYTEA), в которой в сериализованном виде хранятся метаданные типов. Уже на уровне сервера приложений 1С эта бинарная строка десериализуется платформой и превращается в полноценный объект ОписаниеТипов.
Передать такой сложный объект обратно в СУБД через стандартный параметр запроса невозможно, так как транслятор запросов 1С не умеет преобразовывать комплексный объект встроенного языка в понятные для SQL конструкции в секциях ВЫБРАТЬ или ГДЕ. Более того, функция ВЫРАЗИТЬ в запросах 1С является статической директивой компилятора и в принципе не может принимать переменные или параметры в качестве целевого типа данных.
Если вам необходимо сформировать выборку, аналогичную типовой (например, для обмена с сайтом в «1С:Управление торговлей», где требуется наличие заполненного поля ТипЗначения), лучшим решением будет отказаться от попыток получить ОписаниеТипов непосредственно внутри SQL-запроса. Вместо этого выполним постобработку результата во встроенном языке 1С.
Разберем по шагам, как реализовать этот подход. Мы выполним запрос, выгрузим его результат в таблицу значений, добавим колонку с нужным типом и программно заполним её объектами ОписаниеТипов.
Рассмотрим пример кода:
// 1. Создаем и выполняем запрос без попытки передать ОписаниеТипов в параметры
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ДляСвойств.Свойство КАК Свойство,
| ВТ_ДляСвойств.Значение КАК Значение,
| ВТ_ДляСвойств.Заголовок КАК Заголовок,
| NULL КАК ТипЗначения // Создаем пустую колонку-заглушку
|ПОМЕСТИТЬ ВТ_Результат
|ИЗ
| ВТ_ДляСвойств КАК ВТ_ДляСвойств
|;
|ВЫБРАТЬ * ИЗ ВТ_Результат";
РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультата = РезультатЗапроса.Выгрузить();
// 2. Типизируем колонку \"ТипЗначения\" в Таблице Значений
// Для этого заменим колонку-заглушку на колонку с типом ОписаниеТипов
ТаблицаРезультата.Колонки.Удалить("ТипЗначения");
ТаблицаРезультата.Колонки.Добавить("ТипЗначения", Новый ОписаниеТипов("ОписаниеТипов"));
// 3. Программно заполняем колонку нужными описаниями типов
КвалификаторыСтроки = Новый КвалификаторыСтроки(1000);
ОписаниеСтроки = Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки);
Для Каждого СтрокаТаблицы Из ТаблицаРезультата Цикл
// Здесь может быть ваша логика определения типа в зависимости от свойства
СтрокаТаблицы.ТипЗначения = ОписаниеСтроки;
КонецЦикла;
// Теперь таблицу результатов можно передать в типовой механизм обработки
Если вам обязательно нужно передать набор данных с определенными типами внутрь запроса (например, для последующего объединения во временных таблицах), мы можем использовать механизм передачи Таблицы Значений в качестве параметра запроса.
Платформа 1С позволяет передавать ТаблицаЗначений в параметры. При этом транслятор запросов анализирует схему типов (объекты ОписаниеТипов) каждой колонки этой таблицы на сервере приложений и автоматически генерирует в СУБД временную таблицу с физически точными типами полей (например, NVARCHAR(1000) для строк или NUMERIC(15,2) для чисел).
Проанализируем пример создания и передачи такой таблицы:
// Создаем структуру таблицы значений
ТЗ_Параметр = Новый ТаблицаЗначений;
ТЗ_Параметр.Колонки.Добавить("Свойство", Новый ОписаниеТипов("Число"));
// Создаем ОписаниеТипов для строкового поля с квалификаторами
КвалификаторыСтроки = Новый КвалификаторыСтроки(1000);
ОписаниеТиповСтрока = Новый ОписаниеТипов("Строка", , , , КвалификаторыСтроки);
ТЗ_Параметр.Колонки.Добавить("Значение", ОписаниеТиповСтрока);
ТЗ_Параметр.Колонки.Добавить("Заголовок", ОписаниеТиповСтрока);
// Заполняем таблицу данными
НоваяСтрока = ТЗ_Параметр.Добавить();
НоваяСтрока.Свойство = 1;
НоваяСтрока.Значение = "Пример значения";
НоваяСтрока.Заголовок = "Заголовок свойства";
// Передаем таблицу значений в запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Свойство КАК Свойство,
| ТЗ.Значение КАК Значение,
| ТЗ.Заголовок КАК Заголовок
|ПОМЕСТИТЬ ВТ_СвоиСвойства
|ИЗ
| &ТЗ_Параметр КАК ТЗ";
Запрос.УстановитьПараметр("ТЗ_Параметр", ТЗ_Параметр);
Результат = Запрос.Выполнить();
Этот способ избавляет от необходимости использовать ненадежные конструкции ВЫРАЗИТЬ внутри текста запроса и позволяет гибко управлять типами колонок создаваемых временных таблиц непосредственно из кода на встроенном языке.
Бывают ситуации, когда вам не нужно выводить ОписаниеТипов наружу, а требуется лишь отфильтровать данные в запросе по списку разрешенных типов, хранящихся в объекте ОписаниеТипов.
Передать само описание для фильтрации нельзя, но мы можем извлечь из него массив простых объектов Тип с помощью метода Типы(). Платформа 1С отлично справляется с обработкой массивов типов в параметрах запросов.
Посмотрим, как это выглядит на практике:
// Предположим, у нас есть ОписаниеТипов, содержащее несколько разрешенных типов
// Извлекаем массив конкретных типов
МассивРазрешенныхТипов = ОписаниеТипов_Строка.Типы();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ДляСвойств.Свойство КАК Свойство,
| ВТ_ДляСвойств.Значение КАК Значение
|ИЗ
| ВТ_ДляСвойств КАК ВТ_ДляСвойств
|ГДЕ
| ТИПЗНАЧЕНИЯ(ВТ_ДляСвойств.Значение) В (&МассивРазрешенныхТипов)";
Запрос.УстановитьПараметр("МассивРазрешенныхТипов", МассивРазрешенныхТипов);
Результат = Запрос.Выполнить();
Использование конструкции ТИПЗНАЧЕНИЯ(...) В (&МассивРазрешенныхТипов) является наиболее производительным и архитектурно правильным решением для фильтрации по сложным типам данных внутри СУБД.
Попытка передать ОписаниеТипов напрямую в параметр запроса — это классическое нарушение границ абстракции между объектной моделью 1С и реляционной СУБД. Понимание того, как платформа сериализует метаданные типов в бинарные данные СУБД, помогает разработчику проектировать чистый, производительный код без костылей. Рекомендуется использовать постобработку таблиц значений во встроенном языке или передачу типизированных ТЗ в качестве параметров для достижения максимальной гибкости и стабильности ваших решений.