В процессе разработки в среде 1С:Предприятие 8 программисты часто сталкиваются с необходимостью динамического создания таблиц значений (ТЗ) для их последующей передачи в запросы. Одна из типичных задач — создание колонки таблицы значений с определенным ссылочным типом данных на основе имеющейся ссылки (в том числе пустой). Однако простая и, казалось бы, логичная конструкция Новый ОписаниеТипов(ТипЗнч(ПустаяСсылка)) приводит к ошибке выполнения программы. В то же время явное указание строкового типа вида Новый ОписаниеТипов("СправочникСсылка.ИмяСправочника") отрабатывает без нареканий.
В этой статье мы подробно разберем, почему возникает эта ошибка, рассмотрим внутреннее устройство конструктора ОписаниеТипов, изучим несколько эффективных способов решения проблемы и выясним, как правильно выгружать типизированные данные из табличных частей без ручной рутины.
Для начала проанализируем поведение платформы. Когда мы вызываем функцию ТипЗнч(ПустаяСсылка), она возвращает объект специального системного типа Тип. Ошибка кроется в требованиях самого конструктора ОписаниеТипов к передаваемым параметрам.
Обратимся к Системному помощнику (СП). Конструктор объекта ОписаниеТипов имеет следующий синтаксис:
Новый ОписаниеТипов(ИсходныйМассивТипов, КвалификаторыЧисла, КвалификаторыСтроки...) или Новый ОписаниеТипов(СтрокаСИменамиТипов, ...).
Важное ограничение: первый параметр конструктора должен быть либо Массивом (содержащим объекты типа Тип), либо Строкой (в которой через запятую перечислены строковые имена типов). Платформа 1С:Предприятие не выполняет неявное преобразование одиночного объекта Тип в массив из одного элемента. Именно поэтому передача одиночного значения типа Тип напрямую в конструктор вызывает ошибку несоответствия типов.
Теперь рассмотрим рабочие решения этой проблемы.
Самый простой и правильный с точки зрения «чистой» платформы способ — это обернуть полученный тип в массив из одного элемента. Поскольку массив является валидным параметром для конструктора, платформа без проблем создаст нужное описание типов.
Разберем этот метод на примере кода:
// 1. Создаем пустой массив для типов
МассивТипов = Новый Массив;
// 2. Получаем тип нашей ссылки и добавляем его в массив
МассивТипов.Добавить(ТипЗнч(ПустаяСсылкаТипа));
// 3. Создаем ОписаниеТипов на основе подготовленного массива
ОписаниеТипаКолонки = Новый ОписаниеТипов(МассивТипов);
// 4. Добавляем строго типизированную колонку в Таблицу Значений
ТЗн = Новый ТаблицаЗначений;
ТЗн.Колонки.Добавить("Ссылка", ОписаниеТипаКолонки);
Этот способ универсален, работает на любой версии платформы 1С и не требует наличия в конфигурации каких-либо библиотек.
Если разработка ведется в типовой конфигурации (например, 1С:Бухгалтерия, 1С:ERP или 1С:Управление торговлей), то в вашем распоряжении есть мощный инструментарий БСП. В ней уже предусмотрены готовые функции, которые избавляют программиста от необходимости вручную создавать вспомогательные массивы.
Рассмотрим использование функции ОписаниеТипов из общего модуля ОбщегоНазначенияКлиентСервер:
// Функция БСП самостоятельно проверит входящий параметр.
// Если передан одиночный Тип, она сама обернет его в массив и вернет готовый объект ОписаниеТипов.
ОписаниеТипаКолонки = ОбщегоНазначенияКлиентСервер.ОписаниеТипов(ТипЗнч(ПустаяСсылкаТипа));
ТЗн = Новый ТаблицаЗначений;
ТЗн.Колонки.Добавить("Ссылка", ОписаниеТипаКолонки);
Также в БСП есть специализированные функции для получения строкового представления и работы с ссылочными типами данных:
ОбщегоНазначения.СтроковоеПредставлениеТипа(Тип) — возвращает строковое имя типа (например, "СправочникСсылка.Номенклатура"), которое затем можно передать строкой в конструктор.ОбщегоНазначения.ОписаниеТиповВсеСсылки() — создает описание типов, содержащее все ссылочные типы конфигурации (полезно для организации универсальных хранилищ).Иногда требуется решить задачу в «самописной» конфигурации, где БСП отсутствует, а создавать массивы вручную по каким-то причинам не хочется. В этом случае имя типа для конструктора можно сформировать программно, обратившись к метаданным системы.
Проанализируем следующий алгоритм:
// Получаем объект метаданных по типу значения нашей ссылки
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(ПустаяСсылкаТипа));
// Полное имя объекта в метаданных имеет вид "Справочник.КОНФ_ТипыОболочки"
ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
// Для ОписанияТипов нам требуется формат "СправочникСсылка.ИмяСправочника"
// Заменяем точку на разделитель "Ссылка."
ТипСтрокой = СтрЗаменить(ПолноеИмя, ".", "Ссылка.");
// Создаем описание типов по строковому представлению
ТЗн = Новый ТаблицаЗначений;
ТЗн.Колонки.Добавить("Ссылка", Новый ОписаниеТипов(ТипСтрокой));
Важное примечание: Этот способ рабочий, но считается избыточным и менее производительным из-за обращения к структуре метаданных. Рекомендуется отдавать предпочтение первому методу с использованием массива.
Часто задача создания типизированной таблицы значений возникает, когда нам нужно получить часть данных из уже существующего объекта — например, выгрузить из табличной части (ТЧ) документа только те строки, у которых установлен определенный флаг (например, признак «Отметка»).
Многие разработчики совершают ошибку: они вручную создают пустую ТЗ, программно описывают типы ее колонок, затем в цикле обходят строки табличной части и копируют данные. Это неоптимальный подход, увеличивающий объем кода и снижающий скорость работы программы.
Платформа 1С позволяет решить эту задачу гораздо проще и элегантнее с помощью метода Скопировать или связки методов НайтиСтроки и Выгрузить. При этом структура колонок и их типы переносятся в новую ТЗ автоматически!
Разберем этот изящный алгоритм по шагам:
// Шаг 1. Задаем структуру отбора (выбираем только строки с признаком Отметка = Истина)
ПараметрыОтбора = Новый Структура("Отметка", Истина);
// Шаг 2. Получаем массив строк табличной части, подходящих под условие
НайденныеСтроки = ТабличнаяЧастьДокумента.НайтиСтроки(ПараметрыОтбора);
// Шаг 3. Выгружаем данные найденных строк в новую Таблицу Значений.
// Мы указываем список нужных колонок (в нашем примере - "МойРеквизит").
// Новая ТЗн будет создана автоматически, а колонка "МойРеквизит" унаследует
// точное и строгое описание типов из метаданных исходной табличной части!
ТЗн = ТабличнаяЧастьДокумента.Выгрузить(НайденныеСтроки, "МойРеквизит");
Этот способ полностью избавляет программиста от необходимости вручную использовать объект ОписаниеТипов, гарантирует абсолютную точность типов и работает максимально быстро на уровне платформы.
При передаче Таблицы Значений в качестве параметра запроса (для построения временных таблиц через менеджер временных таблиц) строгая типизация колонок приобретает решающее значение.
Если колонка таблицы значений будет иметь составной тип или не будет типизирована вовсе (будет иметь тип Неопределено из-за отсутствия явного описания типа при добавлении колонки), СУБД при попытке выполнить запрос столкнется с неопределенностью. Это приведет к созданию неоптимальных полей в SQL-таблице (например, полей неограниченной длины или дублирующих колонок под каждый тип составного поля) либо вызовет критическую ошибку выполнения: «Несоответствие типов при помещении таблицы значений во временную таблицу».
Поэтому всегда следите за тем, чтобы колонки ТЗ, передаваемые в запросы, имели строго заданный одиночный тип. Если же вам требуется передать в условие запроса простой список значений (например, для конструкции ГДЕ Ссылка В (&СписокСсылок)), то использовать таблицу значений вовсе не обязательно — достаточно передать обычный одномерный Массив с элементами нужного типа, так как для него жесткая типизация на уровне платформы при передаче в параметры запроса не требуется.