Работает ли функция ВЫРАЗИТЬ в соединениях запроса 1С с составными типами для оптимизации?

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

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

Как ВЫРАЗИТЬ транслируется на уровень СУБД

Для начала посмотрим на внутреннюю механику хранения составных типов. Составной тип ссылочных данных (например, Регистратор или ДокументОснование) на уровне базы данных (SQL Server, PostgreSQL) физически хранится в виде нескольких полей. Главные из них — это тип таблицы (в SQL это поле _TRef) и сам уникальный идентификатор ссылки (_RRef).

Когда мы используем в условии соединения конструкцию вида ВЫРАЗИТЬ(Поле КАК Документ.НужныйТип), платформа 1С транслирует это в SQL-запрос как жесткое условие (фильтр) по типу таблицы, добавляя WHERE _TRef = <ТипНужногоДокумента>. Это позволяет оптимизатору СУБД сразу отсечь сканирование индексов и таблиц, не относящихся к указанному типу.

Когда ВЫРАЗИТЬ действительно спасает ситуацию

Рассмотрим подробнее распространенное заблуждение. Многие разработчики считают, что простое сравнение или соединение по ссылке составного типа вызывает проблемы. На самом деле, если мы просто соединяем две таблицы по равенству ссылок, например:


ИЗ РегистрБухгалтерии.Хозрасчетный КАК Регистр 
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.АвансовыйОтчет КАК Документ 
ПО Регистр.Регистратор = Документ.Ссылка

СУБД отлично справится с этой задачей и без явного приведения типов, так как сравнение идет по уникальному идентификатору. Настоящая проблема производительности возникает, когда мы разыменовываем поля (обращаемся к реквизитам через точку) у составного типа.

Проанализируем пример. Если реквизит Регистратор имеет составной тип, включающий 50 видов документов, и мы пишем в условии:

Рег.Регистратор.Номер = ВТ.Номер

В этом случае платформа 1С неявно создаст в итоговом SQL-запросе 50 конструкций LEFT JOIN ко всем возможным таблицам документов, чтобы достать поле "Номер". Запрос получится огромным и крайне неоптимальным. Вот тут нам на помощь и приходит ограничение типа:

ВЫРАЗИТЬ(Рег.Регистратор КАК Документ.НужныйДокумент).Номер = ВТ.Номер

Эта конструкция жестко говорит платформе сделать LEFT JOIN только к одной конкретной таблице документа. Именно здесь кроется колоссальный прирост производительности.

Оптимизация временных таблиц (ВТ)

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

Если же на этапе формирования ВТ (в конструкции ПОМЕСТИТЬ) применить приведение типа, то во временной базе данных создастся колонка строгого, единственного ссылочного типа:


ВЫБРАТЬ
    ВЫРАЗИТЬ(Списание.ДокументОснование КАК Документ.скДопсДСП) КАК ДокРегистратор
ПОМЕСТИТЬ ВТ_Основания
ИЗ
    Документ.СписаниеСРасчетногоСчета КАК Списание

При последующих соединениях (JOIN) других таблиц с этой временной таблицей СУБД сможет построить гораздо более оптимальный план выполнения запроса, так как статистика индексов и размерность данных будут однозначными.

СКД против оптимизатора СУБД

В процессе обсуждения часто возникает путаница: кто именно оптимизирует запрос — Система компоновки данных (СКД) или сам сервер баз данных (SQL)? Выясним причину споров. На самом деле работают оба механизма, но на разных этапах:

  1. СКД собирает итоговый текст запроса до его передачи в СУБД. Если СКД "видит" жесткое ограничение типа через ВЫРАЗИТЬ, она может автоматически исключить лишние таблицы из генерируемого запроса (если их поля не выводятся в конечный отчет).
  2. Оптимизатор СУБД работает уже с переведенным SQL-кодом. Наличие транслированного условия по типу _TRef помогает SQL Server или PostgreSQL использовать механизм Index Seek (быстрый поиск по индексу) вместо тяжелого Index Scan (сканирование всей таблицы).

Альтернативные подходы

Посмотрим на альтернативные варианты решения проблемы соединений с составными типами. Если нам нужно просто отфильтровать записи по определенному типу документа без обращения к его внутренним реквизитам, самым оптимальным вариантом будет использование оператора ССЫЛКА в секции ГДЕ:


ИЗ ВТ1 КАК ВТ1 
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.СписаниеСРасчетногоСчета КАК Списание 
ПО ВТ1.ДокРегистратор = Списание.ДокументОснование 
ГДЕ Списание.ДокументОснование ССЫЛКА Документ.скДопсДСП

Если же в высоконагруженной базе соединение по составному типу становится непреодолимым "узким горлышком", архитекторы иногда идут на денормализацию базы данных. Вместо того чтобы вытягивать реквизит составного типа через медленные соединения, нужный ссылочный реквизит строгого типа добавляется прямо в исходный регистр. Это увеличивает размер базы на диске, но чтение "плоской" таблицы работает гарантированно быстрее любых сложных JOIN-ов.

Подведем итог: Использовать ВЫРАЗИТЬ в соединениях полезно и нужно. Это делает код более читаемым, спасает базу от неявных множественных соединений при разыменовании полей через точку и помогает оптимизатору СУБД строить эффективные планы выполнения запросов.

← На главную