При переходе с файловой информационной базы на клиент-серверный вариант работы разработчики нередко сталкиваются с ситуацией, когда ранее работавший код 1С внезапно падает с системной ошибкой СУБД. При этом в файловой базе тот же самый запрос отрабатывает без нареканий. Проанализируем типичный пример такого запроса, выясним глубинную причину различий в поведении платформ и разберем проверенные варианты корректного написания запросов к полям составного типа.
Рассмотрим исходный запрос, вызывающий ошибку при выполнении на сервере (например, под управлением Microsoft SQL Server):
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Объект
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
| (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение
| И ЗначенияСвойствОбъектов.Свойство = &Свойство
| И (НЕ ЗначенияСвойствОбъектов.Объект.ПометкаУдаления)";
Запрос.УстановитьПараметр("Значение", Название);
Запрос.УстановитьПараметр("Свойство", ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Наименование в сертификате"));
Главная причина кроется в поле Объект регистра сведений ЗначенияСвойствОбъектов. В типовых и отраслевых конфигурациях данное измерение имеет составной тип данных, включающий десятки различных объектов метаданных (справочники Номенклатура, Контрагенты, документы, характеристики и другие сущности).
Разберем различия в обработке такого поля СУБД и файловым движком:
ЗначенияСвойствОбъектов.Объект.ПометкаУдаления) транслируется платформой 1С в генерацию конструкций LEFT OUTER JOIN ко всем физическим таблицам СУБД, типы которых входят в составной тип измерения. В файловой базе разбор выполняет внутренний движок 1С, который может обходить нерелевантные таблицы. В серверной же базе формируется колоссальный SQL-запрос с десятками соединений.The query processor ran out of internal resources and could not produce a query plan.ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50)) на уровне SQL-сервера накладывается на физические колонки составного поля. Если ресурс содержит бинарные данные, ссылки или уникальные идентификаторы, прямое приведение без предварительной проверки типа может приводить к ошибкам преобразования на уровне СУБД.Самый быстрый способ устранить лишние соединения при разыменовании — явно указать платформе, к какой именно таблице нужно обращаться. Для этого применим конструкцию ВЫРАЗИТЬ непосредственно в условии ГДЕ.
Посмотрим на пример исправленного запроса:
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ЗначенияСвойствОбъектов.Свойство КАК Свойство,
| ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Объект
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
| ЗначенияСвойствОбъектов.Свойство = &Свойство
| И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение
| И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура).ПометкаУдаления = ЛОЖЬ)";
В этом случае платформа 1С сформирует соединение только с одной таблицей Справочник.Номенклатура, полностью исключив соединения с остальными типами.
Альтернативный и очень лаконичный вариант — указать платформе тип поля с помощью оператора ССЫЛКА. Как только в секции условий появляется фильтр по конкретному типу ссылки, генератор запросов 1С автоматически отсекает все остальные ветки составного типа для разыменования через точку.
Рассмотрим структуру условий:
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Объект
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
| ЗначенияСвойствОбъектов.Свойство = &Свойство
| И ЗначенияСвойствОбъектов.Объект ССЫЛКА Справочник.Номенклатура
| И НЕ ЗначенияСвойствОбъектов.Объект.ПометкаУдаления
| И ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Значение) = ТИП(СТРОКА)
| И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение";
Обратим внимание: мы добавили проверку ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Значение) = ТИП(СТРОКА). Это гарантирует, что операция ограничения длины строки не будет выполняться для записей, где значение свойства является ссылочным типом.
Наиболее надежный с точки зрения стандартов разработки 1С подход — полный отказ от обращения через точку к полям составного типа и замена его явным внутренним соединением (ВНУТРЕННЕЕ СОЕДИНЕНИЕ).
Разберем по шагам, как строится такой запрос:
ЗначенияСвойствОбъектов с обязательным первичным отбором по измерению Свойство.Справочник.Номенклатура по полю Ссылка.
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Объект
|ИЗ
| РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ЗначенияСвойствОбъектов.Объект = Номенклатура.Ссылка
|ГДЕ
| ЗначенияСвойствОбъектов.Свойство = &Свойство
| И НЕ Номенклатура.ПометкаУдаления
| И ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Значение) = ТИП(СТРОКА)
| И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение";
Преимущества явного соединения:
Ссылка, что обеспечивает максимальную скорость выполнения.Чтобы запрос работал максимально быстро, важно учитывать порядок полей в индексах регистра сведений. В регистре ЗначенияСвойствОбъектов основным ведущим измерением обычно является Объект либо Свойство (в зависимости от структуры регистра в конкретной конфигурации).
Если поиск выполняется по свойству и значению, условие ЗначенияСвойствОбъектов.Свойство = &Свойство должно идти первым в блоке ГДЕ, так как именно по нему СУБД сможет применить индекс и сразу отсечь подавляющую часть записей регистра до выполнения соединений и строковых проверок.