Почему запрос к регистру сведений завершается с ошибкой в серверном режиме и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе с файловой информационной базы на клиент-серверный вариант работы разработчики нередко сталкиваются с ситуацией, когда ранее работавший код 1С внезапно падает с системной ошибкой СУБД. При этом в файловой базе тот же самый запрос отрабатывает без нареканий. Проанализируем типичный пример такого запроса, выясним глубинную причину различий в поведении платформ и разберем проверенные варианты корректного написания запросов к полям составного типа.

Рассмотрим исходный запрос, вызывающий ошибку при выполнении на сервере (например, под управлением Microsoft SQL Server):


Запрос.Текст = 
"ВЫБРАТЬ ПЕРВЫЕ 1
|	ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Объект
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|	(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение
|	И ЗначенияСвойствОбъектов.Свойство = &Свойство
|	И (НЕ ЗначенияСвойствОбъектов.Объект.ПометкаУдаления)";

Запрос.УстановитьПараметр("Значение", Название);
Запрос.УстановитьПараметр("Свойство", ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Наименование в сертификате"));

Почему запрос работает в файловой базе, но падает на сервере

Главная причина кроется в поле Объект регистра сведений ЗначенияСвойствОбъектов. В типовых и отраслевых конфигурациях данное измерение имеет составной тип данных, включающий десятки различных объектов метаданных (справочники Номенклатура, Контрагенты, документы, характеристики и другие сущности).

Разберем различия в обработке такого поля СУБД и файловым движком:

  1. Механизм неявных соединений: Обращение через точку к реквизиту поля составного типа (ЗначенияСвойствОбъектов.Объект.ПометкаУдаления) транслируется платформой 1С в генерацию конструкций LEFT OUTER JOIN ко всем физическим таблицам СУБД, типы которых входят в составной тип измерения. В файловой базе разбор выполняет внутренний движок 1С, который может обходить нерелевантные таблицы. В серверной же базе формируется колоссальный SQL-запрос с десятками соединений.
  2. Ограничения Microsoft SQL Server: В СУБД MS SQL действуют жесткие внутренние лимиты. Например, существует ограничение на максимальное количество таблиц в одном запросе (не более 256). При большом количестве типов и включенных механизмах RLS оптимизатор запросов SQL может исчерпать ресурсы и выдать ошибку вида The query processor ran out of internal resources and could not produce a query plan.
  3. Конфликты прав доступа (RLS): Сервер СУБД вынужден обращаться ко всем соединенным таблицам составного типа. Если у текущего пользователя ограничены права хотя бы на одну из этих таблиц, запрос аварийно завершится, даже если фактически запрашиваются данные только одного справочника.
  4. Особенности приведения типов: Конструкция ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50)) на уровне SQL-сервера накладывается на физические колонки составного поля. Если ресурс содержит бинарные данные, ссылки или уникальные идентификаторы, прямое приведение без предварительной проверки типа может приводить к ошибкам преобразования на уровне СУБД.

Способ 1. Явное приведение составного типа оператором ВЫРАЗИТЬ

Самый быстрый способ устранить лишние соединения при разыменовании — явно указать платформе, к какой именно таблице нужно обращаться. Для этого применим конструкцию ВЫРАЗИТЬ непосредственно в условии ГДЕ.

Посмотрим на пример исправленного запроса:


Запрос.Текст = 
"ВЫБРАТЬ ПЕРВЫЕ 1
|	ЗначенияСвойствОбъектов.Свойство КАК Свойство,
|	ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Объект
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|	ЗначенияСвойствОбъектов.Свойство = &Свойство
|	И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение
|	И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура).ПометкаУдаления = ЛОЖЬ)";

В этом случае платформа 1С сформирует соединение только с одной таблицей Справочник.Номенклатура, полностью исключив соединения с остальными типами.

Способ 2. Ограничение типа через оператор ССЫЛКА

Альтернативный и очень лаконичный вариант — указать платформе тип поля с помощью оператора ССЫЛКА. Как только в секции условий появляется фильтр по конкретному типу ссылки, генератор запросов 1С автоматически отсекает все остальные ветки составного типа для разыменования через точку.

Рассмотрим структуру условий:


Запрос.Текст = 
"ВЫБРАТЬ ПЕРВЫЕ 1
|	ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Объект
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|ГДЕ
|	ЗначенияСвойствОбъектов.Свойство = &Свойство
|	И ЗначенияСвойствОбъектов.Объект ССЫЛКА Справочник.Номенклатура
|	И НЕ ЗначенияСвойствОбъектов.Объект.ПометкаУдаления
|	И ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Значение) = ТИП(СТРОКА)
|	И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение";

Обратим внимание: мы добавили проверку ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Значение) = ТИП(СТРОКА). Это гарантирует, что операция ограничения длины строки не будет выполняться для записей, где значение свойства является ссылочным типом.

Способ 3. Использование явного соединения вместо разыменования

Наиболее надежный с точки зрения стандартов разработки 1С подход — полный отказ от обращения через точку к полям составного типа и замена его явным внутренним соединением (ВНУТРЕННЕЕ СОЕДИНЕНИЕ).

Разберем по шагам, как строится такой запрос:

  1. Выбираем записи из регистра сведений ЗначенияСвойствОбъектов с обязательным первичным отбором по измерению Свойство.
  2. Явно соединяем таблицу регистра с таблицей Справочник.Номенклатура по полю Ссылка.
  3. Условие пометки удаления переносим на соединенную таблицу справочника.

Запрос.Текст = 
"ВЫБРАТЬ ПЕРВЫЕ 1
|	Номенклатура.Ссылка КАК Объект
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
|		ПО ЗначенияСвойствОбъектов.Объект = Номенклатура.Ссылка
|ГДЕ
|	ЗначенияСвойствОбъектов.Свойство = &Свойство
|	И НЕ Номенклатура.ПометкаУдаления
|	И ТИПЗНАЧЕНИЯ(ЗначенияСвойствОбъектов.Значение) = ТИП(СТРОКА)
|	И (ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК СТРОКА(50))) = &Значение";

Преимущества явного соединения:

  1. Исключается случайная генерация неявных внешних соединений.
  2. СУБД использует кластерный индекс таблицы справочника по полю Ссылка, что обеспечивает максимальную скорость выполнения.
  3. План выполнения запроса становится простым и предсказуемым для оптимизатора MS SQL Server.

Рекомендации по оптимизации индексов

Чтобы запрос работал максимально быстро, важно учитывать порядок полей в индексах регистра сведений. В регистре ЗначенияСвойствОбъектов основным ведущим измерением обычно является Объект либо Свойство (в зависимости от структуры регистра в конкретной конфигурации).

Если поиск выполняется по свойству и значению, условие ЗначенияСвойствОбъектов.Свойство = &Свойство должно идти первым в блоке ГДЕ, так как именно по нему СУБД сможет применить индекс и сразу отсечь подавляющую часть записей регистра до выполнения соединений и строковых проверок.

← На главную