При работе с крупными базами данных на платформе 1С:Предприятие 8.3, особенно в конфигурациях на базе ERP-универсальной архитектуры (таких как Управление торговлей 11), разработчики часто сталкиваются с неожиданно низкой производительностью простых, на первый взгляд, запросов. Типичная ситуация: мы пытаемся получить данные о владельце единицы измерения номенклатуры, и запрос, который должен отрабатывать доли секунды, «зависает» на несколько минут. Проанализируем, почему это происходит, и разберем эффективные методы оптимизации.
Рассмотрим классический пример запроса, который вызывает проблемы. Представим ситуацию, когда нам необходимо выбрать всю номенклатуру и данные о владельце её базовой единицы измерения. На первый взгляд, запрос выглядит безобидно:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.ЕдиницаИзмерения.Владелец КАК Владелец,
Номенклатура.ЕдиницаИзмерения.Наименование КАК ЕдиницаИзмерения
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ЕдиницаИзмерения.Владелец ССЫЛКА Справочник.Номенклатура
Проанализируем ситуацию. В конфигурации Управление торговлей 11 реквизит Владелец справочника УпаковкиЕдиницыИзмерения имеет составной тип. Владельцем может выступать как конкретная позиция справочника Номенклатура, так и элемент справочника НаборыУпаковок. Когда мы пишем в запросе Номенклатура.ЕдиницаИзмерения.Владелец, мы совершаем двойное обращение «через точку».
Для СУБД (MS SQL Server или PostgreSQL) это превращается в тяжеловесную конструкцию. Поскольку поле Владелец составное, платформа 1С не знает заранее, из какой таблицы извлекать данные. В результате генерируемый SQL-запрос содержит LEFT OUTER JOIN ко всем таблицам, входящим в этот составной тип. Если составной тип включает 5 справочников, СУБД выполнит 5 соединений. В нашем случае это приводит к избыточному сканированию таблиц, что убивает производительность.
Самый быстрый и правильный способ оптимизации в данном случае — это явное указание типа данных с помощью оператора ВЫРАЗИТЬ. Это позволяет платформе жестко ограничить количество соединений в SQL-запросе только одной нужной таблицей.
Разберем, как изменить наш запрос:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
ВЫРАЗИТЬ(Номенклатура.ЕдиницаИзмерения.Владелец КАК Справочник.Номенклатура) КАК Владелец,
Номенклатура.ЕдиницаИзмерения.Наименование КАК ЕдиницаИзмерения
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
ВЫРАЗИТЬ(Номенклатура.ЕдиницаИзмерения.Владелец КАК Справочник.Номенклатура) ССЫЛКА Справочник.Номенклатура
Что здесь произошло? Применив ВЫРАЗИТЬ(... КАК Справочник.Номенклатура), мы сообщили системе, что нас интересуют данные только из таблицы номенклатуры. Платформа исключит из SQL-плана соединение со справочником НаборыУпаковок и другими возможными типами. Это мгновенно ускоряет выполнение, так как СУБД начинает использовать индексы более эффективно.
В сложных отчетах использование оператора ВЫРАЗИТЬ может быть недостаточно, особенно если номенклатуры в базе сотни тысяч. Рассмотрим более профессиональный подход — использование временных таблиц. Разделим задачу на этапы, чтобы упростить работу оптимизатора СУБД.
Шаг 1: Выберем ссылки номенклатуры и единиц измерения во временную таблицу.
ВЫБРАТЬ
Номенклатура.Ссылка КАК НоменклатураСсылка,
Номенклатура.ЕдиницаИзмерения КАК ЕдИзмСсылка
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
Справочник.Номенклатура КАК Номенклатура
ИНДЕКСИРОВАТЬ ПО
ЕдИзмСсылка
;
Шаг 2: Соединим временную таблицу с основной таблицей единиц измерения, чтобы получить владельца.
ВЫБРАТЬ
ВТ.НоменклатураСсылка КАК Номенклатура,
ЕдИзм.Владелец КАК Владелец,
ЕдИзм.Наименование КАК ЕдиницаНаименование
ИЗ
ВТ_ИсходныеДанные КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.УпаковкиЕдиницыИзмерения КАК ЕдИзм
ПО ВТ.ЕдИзмСсылка = ЕдИзм.Ссылка
ГДЕ
ЕдИзм.Владелец ССЫЛКА Справочник.Номенклатура
Почему этот метод работает лучше? Мы разорвали цепочку обращений «через точку». Платформе больше не нужно строить сложные вложенные соединения. Мы сначала подготовили список ссылок, а затем одним точным соединением получили нужные данные.
Рассмотрим еще один важный аспект. Обращение через точку в секции ГДЕ часто делает запрос non-SARGable. Это означает, что СУБД не может использовать индекс для фильтрации и вынуждена выполнять полное сканирование таблицы (Full Scan). Посмотрим на пример оптимизированного соединения:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
ЕдИзм.Владелец КАК Владелец
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.УпаковкиЕдиницыИзмерения КАК ЕдИзм
ПО Номенклатура.ЕдиницаИзмерения = ЕдИзм.Ссылка
ГДЕ
ВЫРАЗИТЬ(ЕдИзм.Владелец КАК Справочник.Номенклатура) = Номенклатура.Ссылка
В данном примере мы явно соединяем таблицы. Это позволяет нам полностью контролировать процесс объединения данных и избегать «магии» автоматической генерации SQL, которая не всегда оптимальна.
Выясним причину, по которой именно в УТ 11 эта проблема стоит так остро. В старых конфигурациях (например, УПП 1.3 или УТ 10.3) справочник ЕдиницыИзмерения был подчинен справочнику Номенклатура напрямую, и тип владельца был однозначным. В современной архитектуре справочник УпаковкиЕдиницыИзмерения универсален. Он обслуживает и индивидуальные единицы номенклатуры, и общие наборы упаковок для целых товарных категорий.
Именно эта универсальность (наличие составного типа в поле Владелец) и создает нагрузку. Когда вы делаете проверку Номенклатура.ЕдиницаИзмерения.Владелец ССЫЛКА Справочник.Номенклатура, вы фактически заставляете базу данных проверять каждую упаковку на принадлежность к разным типам объектов.
Проанализируем основные правила, которые помогут избежать подобных проблем в будущем:
Объект.Реквизит.Реквизит). Каждый уровень — это потенциальное дополнительное соединение в SQL.ВЫРАЗИТЬ для составных полей: Если вы знаете, что в поле Владелец, Регистратор или в любом другом поле составного типа в данной конкретной задаче лежит определенный справочник — всегда указывайте это явно.ИНДЕКСИРОВАТЬ ПО. Это критично для скорости JOIN.ССЫЛКА в условиях, где это возможно: Иногда проверку типа ССЫЛКА можно заменить на соединение с конкретной таблицей, что даст СУБД больше информации для оптимизации плана.Index Scan (сканирование индекса) там, где ожидается Index Seek (поиск по индексу).Подводя итог, можно сказать, что медленная работа запроса в УТ 11.4 при получении владельца единицы измерения — это классическая архитектурная ловушка. Использование оператора ВЫРАЗИТЬ превращает «бесконечный» запрос в мгновенный, позволяя системе четко понять структуру данных и построить оптимальный SQL-код.