Как оптимизировать медленный запрос для поиска номенклатуры по артикулам и кроссам в 1С?

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

При доработке типового поиска в конфигурации 1С:Управление торговлей 10.3 разработчики часто сталкиваются с существенным падением производительности. Попытка организовать поиск по нескольким словам наименования, артикулам, брендам и дополнительной таблице кроссов нередко приводит к тому, что запрос выполняется по 15–20 секунд. Разберем причины такого поведения платформы и СУБД, а также пошагово реализуем эффективные варианты решения проблемы.

Анализ исходного запроса и причины медленной работы

Рассмотрим структуру исходного запроса, вызывающего длительные задержки. В нем выполняется левое соединение справочника кроссов со справочником Номенклатура с фильтрацией по условию ПОДОБНО и точному совпадению через ОБЪЕДИНИТЬ ВСЕ:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    СправочникНоменклатура.Ссылка КАК Номенклатура,
    СправочникНоменклатура.Артикул,
    СправочникНоменклатура.Родитель,
    СправочникНоменклатура.Код
ИЗ
    Справочник.СайтКроссыТоваров КАК СайтКроссыТоваров
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СправочникНоменклатура
        ПО СайтКроссыТоваров.Артикул = СправочникНоменклатура.ПолеПоиска
        И СайтКроссыТоваров.Бренд = СправочникНоменклатура.Бренд
ГДЕ
    СайтКроссыТоваров.КроссПоисковыйАртикул ПОДОБНО &Представление

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

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

Выделим ключевые узкие места этого решения:

  1. Соединение до фильтрации. В исходном варианте соединение огромной таблицы номенклатуры с таблицей кроссов происходит в каждой ветке объединения. СУБД вынуждена обрабатывать большие объемы связей до того, как будет применен отбор.
  2. Поиск по подстроке с ведущим процентом. Если параметр &Представление начинается с подстановочного знака %, реляционная СУБД принципиально не может использовать индекс B-Tree по реквизиту КроссПоисковыйАртикул. Это приводит к полному сканированию всей таблицы (Table Scan).
  3. Дублирование условий. Если артикул совпадает точно, он гарантированно попадет и под условие ПОДОБНО "%значение%". Выполнять повторное чтение таблицы через ОБЪЕДИНИТЬ ВСЕ по условию точного равенства избыточно.
  4. Соединение по строковым полям. Соединение таблиц по составному условию из строковых полей переменной длины (Артикул и Бренд) выполняется медленно, особенно при отсутствии составного индекса.

Решение 1: Вынесение выборки во временную таблицу с индексацией

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

Перепишем запрос следующим образом:


ВЫБРАТЬ
    СайтКроссыТоваров.Бренд КАК Бренд,
    СайтКроссыТоваров.Артикул КАК Артикул
ПОМЕСТИТЬ втСайтКроссыТоваров
ИЗ
    Справочник.СайтКроссыТоваров КАК СайтКроссыТоваров
ГДЕ
    СайтКроссыТоваров.КроссПоисковыйАртикул ПОДОБНО &Представление

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

ВЫБРАТЬ
    СайтКроссыТоваров.Бренд,
    СайтКроссыТоваров.Артикул
ИЗ
    Справочник.СайтКроссыТоваров КАК СайтКроссыТоваров
ГДЕ
    СайтКроссыТоваров.КроссПоисковыйАртикул = &Крос

ИНДЕКСИРОВАТЬ ПО
    Бренд,
    Артикул
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    СправочникНоменклатура.Ссылка КАК Ссылка,
    СправочникНоменклатура.Артикул КАК Артикул,
    СправочникНоменклатура.Родитель КАК Родитель,
    СправочникНоменклатура.Код КАК Код
ИЗ
    втСайтКроссыТоваров КАК втСайтКроссыТоваров
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СправочникНоменклатура
        ПО втСайтКроссыТоваров.Артикул = СправочникНоменклатура.ПолеПоиска
        И втСайтКроссыТоваров.Бренд = СправочникНоменклатура.Бренд

Обратим внимание на важные правила построения таких запросов:

  1. Конструкция ИНДЕКСИРОВАТЬ ПО. Позволяет СУБД построить индекс по полям временной таблицы перед соединением. Это исключает вложенные циклы сканирования и значительно ускоряет операцию ЛЕВОЕ СОЕДИНЕНИЕ.
  2. Порядок полей в индексе. Если поле Бренд является ссылочным типом, а Артикул — строкой, в секции индексации первым указываем ссылочное поле (Бренд, Артикул). Сравнение ссылок и чисел в индексах СУБД выполняется быстрее, чем строковых данных.
  3. Отказ от оператора ИЛИ в ГДЕ. Попытка объединить два условия через ГДЕ Поле = &Парам ИЛИ Поле ПОДОБНО &Парам2 приводит к невозможности применения индексов в большинстве СУБД. Конструкция с ОБЪЕДИНИТЬ ВСЕ работает быстрее и предсказуемее.

Решение 2: Нормализация поисковых ключей и префиксный поиск

Даже с временными таблицами поиск по подстроке ПОДОБНО "%123%" заставляет сервер сканировать всю таблицу кроссов. Для достижения мгновенного отклика (доли секунды) применим архитектурную оптимизацию:

  1. Создание очищенного реквизита. Добавим в справочники номенклатуры и кроссов строковый реквизит (например, ОчищенныйАртикул) и установим ему свойство Индексировать.
  2. Нормализация при записи. В обработчике ПередЗаписью программно очищаем строку: удаляем пробелы, дефисы, точки, слэши и переводим символы в верхний регистр. При поиске введенную пользователем строку обрабатываем точно так же.
  3. Поиск по префиксу. Если пользователю требуется неполное совпадение, знак процента ставим только в конце: ПОДОБНО &ОчищенныйАртикул + "%". В этом случае СУБД способна использовать индекс (Index Seek), так как начало строки строго фиксировано.
  4. Разделение логики поиска. Сначала выполняем быстрый запрос по точному совпадению очищенного артикула. Если результат найден, завершаем поиск. Если совпадений нет — только тогда запускаем более тяжелый запрос по префиксу или вхождению.

Решение 3: Переход со справочника на регистр сведений

Использование справочника для хранения кросс-кодов создает избыточную нагрузку на дисковую подсистему: справочник хранит ссылки, код, наименование, системные реквизиты и версии данных. Для автокроссов оптимально использовать непериодический независимый регистр сведений со следующей структурой измерений:

  1. КроссАртикулОчищенный (Строка, индексировать);
  2. Бренд (СправочникСсылка.Бренды или Строка);
  3. Номенклатура (СправочникСсылка.Номенклатура).

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

Решение 4: Полнотекстовый поиск для поиска по нескольким словам

Когда требуется искать номенклатуру по произвольной комбинации слов наименования (например, «болт амор подвес Камаз»), использование множественных условий ПОДОБНО в цикле или запросе перегружает сервер. Для таких задач предназначен встроенный механизм полнотекстового поиска 1С:


СписокНайдено = ПолнотекстовыйПоиск.СоздатьСписок("болт* И амор* И подвес* И Камаз*");
СписокНайдено.ОбластьПоиска.Добавить(Метаданные.Справочники.Номенклатура);
СписокНайдено.ПерваяЧасть();

МассивСсылок = Новый Массив;
Для Каждого Элемент Из СписокНайдено Цикл
    МассивСсылок.Добавить(Элемент.Значение);
КонецЦикла;

Механизм полнотекстового поиска использует инвертированный индекс и специализированные словари морфологии, выдавая результат за миллисекунды независимо от объема каталога товаров. При его использовании необходимо настроить регулярное фоновое задание по обновлению индекса методом ПолнотекстовыйПоиск.ОбновитьИндекс().

← На главную