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