Как влияет обращение через точку в запросах 1С на производительность и как его правильно оптимизировать

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

При написании запросов в платформе 1С:Предприятие разработчики часто используют конструкцию вида Документ.Контрагент.ИНН. Начинающие программисты опасаются использовать обращения через несколько точек, считая это абсолютной ошибкой оптимизации. В этой статье мы подробно разберем, как платформа 1С и СУБД обрабатывают такие обращения, в каких случаях обращение «через точку» безопасно, а когда оно способно критически замедлить работу системы.

В чем разница между обращением к обычному и составному типу данных

Рассмотрим, что происходит на физическом уровне при размоноличивании полей. Когда мы обращаемся к реквизиту единичного ссылочного типа (например, поле Контрагент с типом СправочникСсылка.Контрагенты), платформа 1С автоматически добавляет неявное левое соединение (LEFT OUTER JOIN) с таблицей данного справочника.

Проанализируем простой пример запроса:


ВЫБРАТЬ
	Реализация.Контрагент.ИНН КАК КонтрагентИНН
ИЗ
	Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
	Реализация.Ссылка = &Ссылка

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

Как выглядит обращение через точку на уровне СУБД

Посмотрим на трансляцию запросов платформой 1С на уровень SQL-сервера. Разберем сначала простой ссылочный тип. Для первого примера СУБД сгенерирует следующий лаконичный SQL-код:


SELECT
	T2._Code AS ИНН
FROM
	_Document234 T1
LEFT OUTER JOIN
	_Reference115 T2 ON T1._Fld4520 = T2._IDRRef
WHERE
	T1._IDRRef = 0xBFBECFADC0E8B42A11E4F2F476A97D5D

План выполнения такого запроса содержит простой NESTED OUTER LOOP с точечным поиском по индексу, что работает максимально быстро и эффективно.

Теперь проанализируем пример с составным типом без приведения типа:


ВЫБРАТЬ
	ТоварыОбороты.Регистратор.Контрагент.ИНН КАК ИНН
ИЗ
	РегистрНакопления.ТоварыНаСкладах.Обороты(&НачДата, &КонДата, Регистратор) КАК ТоварыОбороты

Поскольку регистратором могут выступать десятки различных документов, платформа сгенерирует громоздкий SQL-запрос с массой соединений и условным оператором CASE WHEN:


SELECT
	CASE 
		WHEN T1.RecorderTRef = 0x0000009D THEN T4._Fld1200
		WHEN T1.RecorderTRef = 0x0000002D THEN T6._Fld1200
		WHEN T1.RecorderTRef = 0x00000057 THEN T8._Fld1200
		ELSE CAST(NULL AS NVARCHAR(12))
	END
FROM T1
LEFT OUTER JOIN Документ_СчетНаОплату T3 ON T1.RecorderTRef = 0x0000009D AND T1.RecorderRRef = T3._IDRRef
LEFT OUTER JOIN Справочник_Контрагенты T4 ON T3._Fld500 = T4._IDRRef
LEFT OUTER JOIN Документ_ПоступлениеТоваров T5 ON T1.RecorderTRef = 0x0000002D AND T1.RecorderRRef = T5._IDRRef
LEFT OUTER JOIN Справочник_Контрагенты T6 ON T5._Fld500 = T6._IDRRef
LEFT OUTER JOIN Документ_РеализацияТоваров T7 ON T1.RecorderTRef = 0x00000057 AND T1.RecorderRRef = T7._IDRRef
LEFT OUTER JOIN Справочник_Контрагенты T8 ON T7._Fld500 = T8._IDRRef

Такой запрос заставляет СУБД считывать данные из всех таблиц документов, существующих в конфигурации для данного регистра. Это приводит к выполнению лишних сканирований таблиц (Table Scan / Index Scan), колоссальному расходу оперативной памяти и задержкам при выполнении.

Обязательное использование оператора ВЫРАЗИТЬ

Чтобы избежать соединения со всеми таблицами составного типа, необходимо жестко ограничить тип данных с помощью функции ВЫРАЗИТЬ. Выясним, как правильно изменить запрос:


ВЫБРАТЬ
	ВЫРАЗИТЬ(ТоварыОбороты.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент.ИНН КАК ИНН
ИЗ
	РегистрНакопления.ТоварыНаСкладах.Обороты(&НачДата, &КонДата, Регистратор) КАК ТоварыОбороты

Благодаря явному приведению типа платформа свяжет таблицу оборотов только с одной физической таблицей документа РеализацияТоваровУслуг и справочника Контрагенты, полностью исключив остальные варианты.

Важно: Применение функции ВЫРАЗИТЬ требуется не только в секции ВЫБРАТЬ, но и в секциях ГДЕ, ПО, ГРУППИРОВАТЬ ПО и УПОРЯДОЧИТЬ ПО. Если обратиться к составному полю через точку в условии отбора без приведения типа, СУБД сгенерирует соединение со всеми таблицами еще до наложения фильтра.

Дополнительные узкие места при использовании точек

Разберем еще несколько критических сценариев, о которых важно помнить при разработке систем на 1С:

  1. Параметры виртуальных таблиц: Использование обращений через точку внутри параметров виртуальных таблиц (например, в отборах ОстаткиИОбороты или СрезПоследних) является прямым нарушением стандартов 1С. Платформа не сможет применить такой отбор при первичной выборке, из-за чего СУБД сформирует всю таблицу целиком и только затем наложит фильтр.
  2. Влияние на RLS (ограничения доступа): При неявном левом соединении платформа автоматически подтягивает правила RLS, настроенные для целевой таблицы. Если у пользователя ограничены права, простой запрос с обращениями через точку может превратиться в тяжелейший каскад вложенных подзапросов с проверкой прав.
  3. Неявное превращение в INNER JOIN: Если поле, полученное через точку, используется в секции ГДЕ (например, ГДЕ Договор.Контрагент.ИНН = &ИНН), оптимизатор СУБД превращает неявное LEFT JOIN в INNER JOIN. Если ключевые поля не проиндексированы, порядок соединения таблиц может стать неоптимальным.
  4. Глубокая вложенность (более 3–4 точек): Каждая точка в цепочке вида Договор.ФизЛицо.ФизическоеЛицо.Наименование создает новую таблицу в SQL-запросе. При глубокой вложенности оптимизатор СУБД тратит на компиляцию и построение плана выполнения больше времени, чем на сам поиск данных.

Практический пример оптимизации сложного запроса

Рассмотрим реальную задачу. Имеется документ с реквизитами организации, контрагента, партнера и физических лиц, из которых требуется извлечь контактную информацию и реквизиты. Исходный запрос содержит множество обращений через 2–3 точки к связанным справочникам.

Проанализируем оптимальный подход: вместо раздувания запроса неявными соединениями мы выгружаем ключевые ссылки во временную таблицу и затем выполняем явные левые соединения.

Рассмотрим пример формирования оптимизированного запроса:


// 1. Помещаем исходные параметры во временную таблицу
ВЫБРАТЬ
	Договор.Контрагент КАК Контрагент,
	Договор.Партнер КАК Партнер,
	Договор.Организация КАК Организация
ПОМЕСТИТЬ ВТ_ДанныеДокумента
ИЗ
	Документ.ДоговорСтроительноМонтажныхРабот КАК Договор
ГДЕ
	Договор.Ссылка = &Ссылка;

////////////////////////////////////////////////////////////////////////////////
// 2. Извлекаем контактную информацию контрагента и партнера через объединение
ВЫБРАТЬ
	Контакты.Представление КАК Представление,
	Контакты.Вид.Наименование КАК Вид
ПОМЕСТИТЬ ВТ_Контакты
ИЗ
	ВТ_ДанныеДокумента КАК ДанныеДокумента
	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК Контакты
		ПО ДанныеДокумента.Контрагент = Контакты.Ссылка

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	Контакты.Представление,
	Контакты.Вид.Наименование
ИЗ
	ВТ_ДанныеДокумента КАК ДанныеДокумента
	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Партнеры.КонтактнаяИнформация КАК Контакты
		ПО ДанныеДокумента.Партнер = Контакты.Ссылка;

////////////////////////////////////////////////////////////////////////////////
// 3. Выбираем итоговый результат
ВЫБРАТЬ РАЗЛИЧНЫЕ
	Контакты.Представление КАК Представление,
	Контакты.Вид КАК Вид
ИЗ
	ВТ_Контакты КАК Контакты

Подведем итоги: обращаться через точку к простым ссылочным типам в секции ВЫБРАТЬ можно без опасений — платформа построит обычное соединение. Однако при работе с составными типами обязателен оператор ВЫРАЗИТЬ, а в сложных выборках с глубокой вложенностью всегда предпочтительнее использовать временные таблицы и явные соединения.

← На главную