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

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

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

Постановка задачи и анализ ситуации

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

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

Способ №1. Использование конструкции ОБЪЕДИНИТЬ ВСЕ с расстановкой приоритетов

Рассмотрим первый и наиболее очевидный способ — объединение нескольких запросов в один. Этот метод позволяет четко разделить условия поиска. Для того чтобы выбрать «самый правильный» результат из всех найденных, мы введем искусственное поле Приоритет.

Разберем пример построения такого запроса:


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

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

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

УПОРЯДОЧИТЬ ПО
    Приоритет ВОЗР

В данном случае мы используем ОБЪЕДИНИТЬ ВСЕ, так как это работает быстрее, чем просто ОБЪЕДИНИТЬ (последнее заставляет СУБД проверять строки на уникальность, что нам здесь не требуется). Секция УПОРЯДОЧИТЬ ПО Приоритет ВОЗР гарантирует, что если поиск по первому коду (приоритет 1) увенчался успехом, то именно эта строка окажется первой. Инструкция ВЫБРАТЬ ПЕРВЫЕ 1 отсечет все второстепенные результаты.

Способ №2. Условное упорядочивание (ВЫБОР КОГДА)

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

Проанализируем реализацию этого подхода:


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

Этот метод значительно короче и легче в поддержке. Однако стоит помнить о производительности. Использование конструкции ВЫБОР в УПОРЯДОЧИТЬ ПО может помешать СУБД эффективно использовать индексы, если таблица содержит миллионы записей. Впрочем, для большинства прикладных задач в 1С это решение является оптимальным по соотношению «читаемость/скорость».

Проблема разыменования полей через точку

Обратим внимание на важное замечание из обсуждения: использование Дома.ВидОбъекта.Код в условиях запроса. В терминах 1С и SQL это называется «разыменованием ссылочного поля». При выполнении такого запроса платформа автоматически создает LEFT JOIN к таблице справочника ВидыОбъектов.

Выясним причину, почему это может быть проблемой:

  1. Производительность: Каждое такое обращение — это дополнительное соединение таблиц на уровне СУБД.
  2. Данные: Если поле ВидОбъекта не заполнено (равно NULL или пустой ссылке), условие по коду может отсечь нужную запись.

Лучшей практикой считается передача в параметры запроса не строковых кодов, а самих ссылок на элементы справочника ВидОбъекта. Если это невозможно (например, коды приходят из внешней системы), постарайтесь предварительно найти ссылки по этим кодам и передать их в запрос.

Как понять, какой вариант сработал?

Иногда программисту важно знать не только результат, но и то, насколько «точным» он был. В контексте задачи автора, если сработал 2-й или 3-й вариант поиска, нужно вывести предупреждение пользователю.

Рассмотрим, как это реализовать, добавив в выборку служебное поле:


РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Выборка.Следующий();
    
    Если Выборка.ВесРезультата > 1 Тогда
        Сообщить("Внимание: объект найден по запасному варианту кода. Требуется уточнение!");
        // Дополнительная логика подкрашивания формы
    КонецЕсли;
    
    Возврат Выборка.Ссылка;
КонецЕсли;

Такой подход позволяет избежать повторных проверок найденного объекта в коде 1С, так как запрос уже вернул нам всю необходимую информацию о «качестве» совпадения.

Продвинутый метод: Временные таблицы и приоритеты

Если количество условий поиска велико (например, более 10) или они динамически меняются, разберем способ с использованием временной таблицы параметров. Мы можем создать временную таблицу, в которую поместим пары «ЗначениеПоиска» и «Приоритет», а затем соединим её с основной таблицей справочника.

Посмотрим на алгоритм действий:

  1. Создаем таблицу значений в коде 1С с колонками Код и Приоритет.
  2. Загружаем эту таблицу в МенеджерВременныхТаблиц запроса.
  3. В основном запросе выполняем ВНУТРЕННЕЕ СОЕДИНЕНИЕ справочника с этой таблицей по полю Код.
  4. Упорядочиваем результат по приоритету из временной таблицы.

Это наиболее масштабируемый вариант, который позволяет добавлять новые уровни поиска без изменения самого текста запроса.

Резюме

Мы проанализировали несколько подходов к замене параметров и каскадному поиску в запросах. Подведем итоги:

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

← На главную