Работа с полями составного типа в запросах 1С — это классическая «ловушка» для разработчиков, особенно при обращении к таким реквизитам, как Регистратор, Владелец или ДокументОснование. Если не уделять внимания типизации, запрос может выполняться недопустимо долго, а в некоторых случаях даже приводить к аварийному завершению сеанса SQL-сервера. В данной статье мы подробно разберем, почему «обращение через точку» к составному полю замедляет систему, и как переписать запрос так, чтобы он работал максимально эффективно.
Разберем внутренний механизм работы платформы. Когда мы пишем в запросе конструкцию вида Регистратор.Дата, и поле Регистратор имеет составной тип (например, может содержать 50 различных видов документов), платформа 1С при генерации итогового SQL-запроса вынуждена выполнить левое соединение (LEFT OUTER JOIN) со всеми 50 таблицами этих документов. Это явление часто называют «взрывом» запроса.
Проанализируем последствия такого «взрыва»:
Самый простой и стандартный способ оптимизации — использование функции ВЫРАЗИТЬ. Она сообщает платформе, что в данной точке запроса нас интересует только один конкретный тип из всех возможных. Это позволяет платформе исключить лишние LEFT JOIN к другим таблицам.
Рассмотрим пример правильной типизации во временных таблицах. Важно помнить: если мы просто отфильтровали данные по типу через ССЫЛКА, но не применили ВЫРАЗИТЬ при выборе поля, платформа всё равно может оставить лишние соединения в последующих запросах пакета.
ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.Регистратор КАК Регистратор,
ХозрасчетныйОборотыДтКт.СуммаОборот КАК Сумма
ПОМЕСТИТЬ ВТ_ДанныеРегистра
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(&НачалоПериода, &КонецПериода, , , , , ) КАК ХозрасчетныйОборотыДтКт
ГДЕ
ХозрасчетныйОборотыДтКт.Регистратор ССЫЛКА Документ.СчетФактураВыданный;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
// Обязательно типизируем поле при обращении к реквизитам
ВЫРАЗИТЬ(ВТ_ДанныеРегистра.Регистратор КАК Документ.СчетФактураВыданный).ВидСчетаФактуры КАК ВидСФ,
ВТ_ДанныеРегистра.Сумма
ИЗ
ВТ_ДанныеРегистра КАК ВТ_ДанныеРегистра
Важный нюанс: Как отмечают эксперты, даже фильтрация через ССЫЛКА не является для платформы 1С указанием на то, что поле теперь имеет только один тип. Явным ограничением типа для генератора SQL-запросов является только оператор ВЫРАЗИТЬ в списке выбираемых полей или условиях.
В обсуждении часто всплывает вопрос: стоит ли дробить запрос на множество временных таблиц (ВТ)? Рассмотрим стратегию «мега-предусмотрительного» подхода, когда на каждом этапе мы фильтруем и типизируем данные.
Проанализируем ситуацию, когда нам нужно достать данные из «второго уровня» вложенности: Регистратор.ДокументОснование.ДоговорКонтрагента. Здесь у нас сразу два составных поля. Оптимальная схема действий выглядит так:
Регистратор ССЫЛКА Документ.СчетФактураВыданный.Регистратору через ВЫРАЗИТЬ, чтобы получить ДокументОснование.ДокументОснование (например, КАК Документ.ПоступлениеНаРасчетныйСчет).ДоговорКонтрагента.Пример реализации такого подхода:
ВЫБРАТЬ
ВЫРАЗИТЬ(Рег.Регистратор КАК Документ.СчетФактураВыданный) КАК СсылкаСФ,
Рег.СуммаОборот КАК Сумма
ПОМЕСТИТЬ ВТ_СФ
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(..., Регистратор ССЫЛКА Документ.СчетФактураВыданный, ...) КАК Рег;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_СФ.СсылкаСФ КАК СсылкаСФ,
ВЫРАЗИТЬ(ВТ_СФ.СсылкаСФ.ДокументОснование КАК Документ.ПоступлениеНаРасчетныйСчет) КАК Основание,
ВТ_СФ.Сумма
ПОМЕСТИТЬ ВТ_Основания
ИЗ
ВТ_СФ КАК ВТ_СФ
ГДЕ
ВТ_СФ.СсылкаСФ.ВидСчетаФактуры = ЗНАЧЕНИЕ(Перечисление.ВидСчетаФактурыВыставленного.НаАванс)
И ВТ_СФ.СсылкаСФ.ДокументОснование ССЫЛКА Документ.ПоступлениеНаРасчетныйСчет;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Основания.СсылкаСФ,
ВТ_Основания.Основание.ДоговорКонтрагента КАК Договор,
ВТ_Основания.Сумма
ИЗ
ВТ_Основания КАК ВТ_Основания
Выясним причину, по которой даже ВЫРАЗИТЬ иногда проигрывает. Несмотря на типизацию, платформа всё равно использует LEFT JOIN. Если мы точно знаем, что нам нужны данные только из определенных документов, явное внутреннее соединение (INNER JOIN) будет работать быстрее всего.
Рассмотрим рекомендации по переходу от виртуальных таблиц к реальным в высоконагруженных системах:
Хозрасчетный.ДвиженияССубконто) вместо виртуальной таблицы Обороты может быть эффективнее, если вы используете стандартные индексы (Период + Счет + ...).Документ.СчетФактураВыданный по полю Ссылка = Регистратор позволит SQL-серверу использовать INNER JOIN и максимально эффективно применить индексы документа (например, по ДокументОснование или ВидСчетаФактуры).Посмотрим на пример такого «ручного» соединения:
ВЫБРАТЬ
Движения.Регистратор,
ТаблицаСФ.ДокументОснование,
Движения.Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Движения
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.СчетФактураВыданный КАК ТаблицаСФ
ПО Движения.Регистратор = ТаблицаСФ.Ссылка
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеНаРасчетныйСчет КАК ТаблицаОснование
ПО ТаблицаСФ.ДокументОснование = ТаблицаОснование.Ссылка
ГДЕ
Движения.Период МЕЖДУ &НачалоПериода И &КонецПериода
И ТаблицаСФ.ВидСчетаФактуры = ЗНАЧЕНИЕ(Перечисление.ВидСчетаФактурыВыставленного.НаАванс)
И Движения.СчетДт = &СчетНДС
Проанализируем ситуацию с точки зрения структуры базы данных. В типовых конфигурациях (например, БП 3.0) реквизиты документов часто уже индексированы. Например, поле ВидСчетаФактуры и ДокументОснование в СчетФактураВыданный имеют индексы.
Чтобы эти индексы «заиграли» в вашем запросе, старайтесь:
ТИПЗНАЧЕНИЯ() в секции ГДЕ, заменяя её на оператор ССЫЛКА. ССЫЛКА транслируется в SQL проще и быстрее.Регистратор, добавьте ИНДЕКСИРОВАТЬ ПО Регистратор в описание ВТ.ВЫРАЗИТЬ.Мы рассмотрели три основных подхода к оптимизации. Выясним, какой из них выбрать:
ВЫРАЗИТЬ(Регистратор КАК Документ.Тип). Это защитит от «взрыва» запроса и будет вполне читаемо.ССЫЛКА) и типизация (ВЫРАЗИТЬ).Соблюдение этих правил позволит вашим запросам работать стабильно и быстро даже при значительном росте объема данных в информационной базе.