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