Как оптимизировать запрос к продажам и свойствам объектов в 1С:УТ 10.3?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С:Предприятие часто возникают ситуации, когда простой на первый взгляд запрос начинает «тормозить» на реальных объемах данных или выдавать некорректные результаты. Особенно это актуально для конфигураций на обычных формах, таких как 1С:Управление торговлей 10.3, где активно используются механизмы свойств и категорий. Разберем типичную задачу: получение данных о продажах номенклатуры в разрезе определенного дополнительного свойства. Проанализируем ошибки в исходном запросе и выработаем максимально эффективное решение.

Анализ исходной проблемы и выявление ошибок

Рассмотрим исходный текст запроса, который вызвал затруднения у автора:


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

СГРУППИРОВАТЬ ПО
	ПродажиОбороты.Номенклатура,
	ЗначенияСвойствОбъектов.Значение

При внимательном изучении мы можем выделить несколько критических моментов, которые замедляют выполнение и считаются «плохим тоном» в разработке 1С:

  1. Прямое соединение с виртуальной таблицей: Соединение (особенно ЛЕВОЕ СОЕДИНЕНИЕ) виртуальной таблицы Обороты с физической таблицей регистра сведений — это серьезная ошибка. SQL-оптимизатор часто не может построить эффективный план выполнения для такого «бутерброда», что ведет к сканированию лишних индексов или даже всей таблицы.
  2. Обращение через точку к реквизитам составного типа: Поле Объект в регистре ЗначенияСвойствОбъектов имеет составной тип (Справочник.Номенклатура, Справочник.Контрагенты и др.). Обращение ЗначенияСвойствОбъектов.Свойство.Код заставляет систему генерировать дополнительные JOIN на уровне СУБД ко всем таблицам, входящим в тип, что кратно замедляет работу.
  3. Фильтрация по коду в теле запроса: Использование жестко прописанного строкового кода "000000978" делает запрос негибким и медленным. Сравнение строк в SQL всегда медленнее, чем сравнение ссылок (уникальных идентификаторов).
  4. Логическая ошибка соединения: Использование условия ГДЕ по правой таблице в левом соединении автоматически превращает его во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если у товара не заполнено свойство, он просто не попадет в отчет, хотя ЛЕВОЕ СОЕДИНЕНИЕ подразумевает обратное.

Решение №1: Использование временных таблиц и параметров

Разберем наиболее правильный и масштабируемый подход. Основная идея заключается в том, чтобы сначала подготовить данные о продажах, затем подготовить данные о свойствах, и только после этого соединить две небольшие «материализованные» временные таблицы. Выясним, как это реализовать по шагам.

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

Шаг 2. Получение свойств. Выберем только нужные нам свойства номенклатуры, сразу отфильтровав их по ссылке на свойство, переданной в параметре.

Шаг 3. Итоговое соединение. Соединим подготовленные данные.


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

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

////////////////////////////////////////////////////////////////////////////////
// Финальная выборка с индексацией
ВЫБРАТЬ
	ВТ_Свойства.ВидТовара КАК ВидТовара,
	ВТ_Продажи.Номенклатура КАК Номенклатура,
	СУММА(ВТ_Продажи.Сумма) КАК Сумма
ИЗ
	ВТ_Продажи КАК ВТ_Продажи
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Свойства КАК ВТ_Свойства
		ПО ВТ_Продажи.Номенклатура = ВТ_Свойства.Номенклатура

СГРУППИРОВАТЬ ПО
	ВТ_Свойства.ВидТовара,
	ВТ_Продажи.Номенклатура

Решение №2: Оптимизация через первичные таблицы (для огромных баз)

Проанализируем ситуацию, когда база данных содержит миллионы записей. В некоторых случаях работа напрямую с физической таблицей регистра накопления (вместо виртуальной таблицы Обороты) может быть быстрее, если мы правильно накладываем фильтры по индексам. Посмотрим на пример такого подхода, где мы сначала выбираем нужную номенклатуру по свойству, индексируем её, а затем «приклеиваем» к ней продажи.

Рассмотрим этот метод подробнее:


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_НоменклатураСоСвойством.ВидТовара КАК ВидТовара,
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.СтоимостьОборот КАК Сумма
ИЗ
    РегистрНакопления.Продажи.Обороты(&ДатаНачала, &ДатаОкончания, , 
        Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_НоменклатураСоСвойством)) КАК ПродажиОбороты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НоменклатураСоСвойством КАК ВТ_НоменклатураСоСвойством
    ПО ПродажиОбороты.Номенклатура = ВТ_НоменклатураСоСвойством.Номенклатура
ИТОГИ
    СУММА(Сумма)
ПО
    ВидТовара

В данном примере мы используем важный прием — фильтрацию внутри параметров виртуальной таблицы с помощью вложенного запроса к проиндексированной временной таблице. Это позволяет 1С передать на уровень SQL конкретный список идентификаторов номенклатуры, что значительно сужает область поиска в таблицах итогов.

Почему это работает быстрее? Экскурс в теорию

Давайте разберем технические причины, по которым предложенные изменения ускоряют систему:

Подведение итогов

Подводя итог, можно сказать, что «секрет» быстрого запроса в 1С 8.3 (и тем более в старой доброй УТ 10.3) заключается в декомпозиции задачи. Не пытайтесь получить все данные одним сложным запросом с множеством соединений. Построение цепочки из временных таблиц с предварительной фильтрацией и обязательной индексацией полей связи — это золотой стандарт разработки.

Основные правила, которые мы усвоили:

  1. Никогда не соединяйте виртуальные таблицы напрямую с другими таблицами.
  2. Используйте параметры вместо текстовых кодов или наименований.
  3. Всегда типизируйте составные поля через ВЫРАЗИТЬ или ССЫЛКА.
  4. Индексируйте временные таблицы по полям, по которым происходит соединение.
  5. При левом соединении помните о функции ЕстьNULL, если данные могут отсутствовать.

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

← На главную