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