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

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

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

Решение 1. Использование оператора равенства против оператора ПОДОБНО

В процессе обсуждения на форумах часто возникает дилемма: какой оператор использовать для сопоставления строкового кода в запросе — знак равенства = или оператор сравнения с шаблоном ПОДОБНО? Давайте разберем по шагам, как работают эти операторы на уровне СУБД, и выясним причину, почему выбор одного из них может существенно повлиять на скорость выполнения вашего отчета.

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


ВЫБОР 
    КОГДА ТиповойОбороты.Субконто1.Код = "0000000023" 
        ТОГДА НакладныеКонтрагенты.Контрагент 
    ИНАЧЕ ТиповойОбороты.Субконто1 
КОНЕЦ КАК Субконто1

Если мы используем оператор равенства =, платформа транслирует этот запрос в СУБД с требованием выполнить точечный поиск (Index Seek). Поскольку реквизит Код у справочников в 1С практически всегда является индексируемым по умолчанию, СУБД моментально находит нужную запись по индексу, не сканируя всю таблицу целиком. Это самый быстрый и предпочтительный способ поиска данных.

Если же мы решим применить оператор ПОДОБНО:


ВЫБОР 
    КОГДА ТиповойОбороты.Субконто1.Код ПОДОБНО "0000000023" 
        ТОГДА НакладныеКонтрагенты.Контрагент 
    ИНАЧЕ ТиповойОбороты.Субконто1 
КОНЕЦ КАК Субконто1

В этом случае поведение системы будет следующим:

  1. Если шаблон поиска не содержит специальных символов маски (таких как % или _), современные СУБД (например, MS SQL Server) попытаются оптимизировать запрос и неявно привести его к оператору равенства. Однако такое поведение не гарантируется на всех СУБД (например, PostgreSQL) и сильно зависит от настроек сервера и версии платформы.
  2. Если в запросе случайно или намеренно будет использована маска в начале строки (например, ПОДОБНО "%0000000023"), использование индекса по коду станет физически невозможным. СУБД будет вынуждена выполнить полное сканирование всей таблицы справочника (Full Table Scan или Index Scan). На больших базах данных с миллионами контрагентов такой запрос приведет к долгому зависанию и блокировкам таблиц.
  3. Оператор ПОДОБНО работает исключительно со строковыми типами данных. Если в вашей конфигурации тип кода справочника настроен как Число, использование оператора ПОДОБНО вызовет аварийное завершение запроса с ошибкой несовместимости типов. В то же время оператор = абсолютно универсален и корректно отработает как со строками, так и с числами.

Вывод: Всегда используйте оператор равенства =, когда вам нужно строгое соответствие конкретному значению кода.

Решение 2. Оптимизация запроса при работе с составными типами (Конструкция ВЫРАЗИТЬ)

Теперь обратим внимание на самый критический аспект исходной задачи, который часто упускают из виду начинающие разработчики. Поле Субконто1 в регистре бухгалтерии ТиповойОбороты имеет составной тип данных. Оно может одновременно хранить ссылки на справочники «Контрагенты», «Договоры», «Физические лица», «Основные средства» и многие другие.

Когда мы пишем в запросе прямое обращение через точку к реквизиту составного поля (например, ТиповойОбороты.Субконто1.Код), происходит явление, называемое неявным разыменованием полей составного типа.

Проанализируем, что происходит на уровне СУБД под капотом:

Для того чтобы извлечь реквизит Код, платформа 1С вынуждена сгенерировать SQL-запрос, содержащий неявные соединения (LEFT JOIN) абсолютно со всеми таблицами баз данных, которые входят в составной тип этого субконто и имеют реквизит Код. Если на счете настроено субконто, содержащее десяток разных типов справочников, СУБД выполнит десять тяжелых соединений таблиц «на лету» только ради того, чтобы проверить код одного контрагента. Производительность такого запроса падает в разы.

Чтобы избежать этой проблемы, мы обязательно должны использовать конструкцию языка запросов ВЫРАЗИТЬ. Она указывает платформе и СУБД, что в данном месте запроса нас интересует вполне конкретный тип данных. Оптимальный вариант написания условия будет выглядеть так:


ВЫБОР 
    КОГДА ВЫРАЗИТЬ(ТиповойОбороты.Субконто1 КАК Справочник.Контрагенты).Код = "0000000023" 
        ТОГДА НакладныеКонтрагенты.Контрагент 
    ИНАЧЕ ТиповойОбороты.Субконто1 
КОНЕЦ КАК Субконто1

Применив функцию ВЫРАЗИТЬ, мы жестко ограничили круг поиска. Теперь СУБД выполнит левое соединение только с одной-единственной таблицей — справочником «Контрагенты», проигнорировав все остальные типы, входящие в составное поле. Это гарантирует высокую скорость выполнения запроса даже на высоконагруженных базах данных.

Решение 3. Устранение жесткого кодирования (Хардкода) по стандартам 1С

Прописывание текстовых или числовых кодов конкретных элементов (таких как "0000000023") непосредственно внутри текста запроса считается грубым нарушением стандартов разработки фирмы «1С». Данный подход («хардкод») несет в себе серьезные архитектурные риски:

Рассмотрим два правильных варианта решения этой проблемы в соответствии с требованиями 1С:Совместимо.

Способ А. Передача ссылки через параметры запроса

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


// Находим ссылку на контрагента по его коду в программном коде
КонтрагентСсылка = Справочники.Контрагенты.НайтиПоКоду("0000000023");

Если НЕ КонтрагентСсылка.Пустая() Тогда
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ВЫБОР 
        |       КОГДА ТиповойОбороты.Субконто1 = &НужныйКонтрагент 
        |           ТОГДА НакладныеКонтрагенты.Контрагент 
        |       ИНАЧЕ ТиповойОбороты.Субконто1 
        |   КОНЕЦ КАК Субконто1
        |ИЗ
        |   РегистрБухгалтерии.Типовой.Обороты КАК ТиповойОбороты";
        
    Запрос.УстановитьПараметр("НужныйКонтрагент", КонтрагентСсылка);
    РезультатЗапроса = Запрос.Выполнить();
КонецЕсли;

Сравнение ссылочных типов в самом запросе (Субконто1 = &НужныйКонтрагент) выполняется СУБД мгновенно, так как сравнение происходит по внутреннему уникальному идентификатору (UUID), который всегда проиндексирован.

Способ Б. Использование предопределенных элементов

Если логика вашей конфигурации жестко завязана на существование конкретного контрагента, самым надежным и верным архитектурным решением будет создание предопределенного элемента в конфигураторе (например, с именем ФизическоеЛицо). В этом случае в запросе можно будет напрямую обратиться к метаданным с помощью конструкции ЗНАЧЕНИЕ:


ВЫБОР 
    КОГДА ТиповойОбороты.Субконто1 = ЗНАЧЕНИЕ(Справочник.Контрагенты.ФизическоеЛицо) 
        ТОГДА НакладныеКонтрагенты.Контрагент 
    ИНАЧЕ ТиповойОбороты.Субконто1 
КОНЕЦ КАК Субконто1

Этот способ полностью защищен от изменения кодов пользователями в пользовательском режиме и гарантирует работоспособность запроса на любых копиях баз данных.

Решение 4. Особенности реализации задачи в СКД (Системе компоновки данных)

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

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

  1. Параметры схемы компоновки данных. Вместо жесткого написания условия в коде запроса, добавьте на вкладке «Параметры» схемы компоновки данных новый параметр (например, &ИскомыйКонтрагент) с типом значения СправочникСсылка.Контрагенты. В самом тексте запроса СКД укажите гибкое условие сравнения по ссылке: КОГДА ТиповойОбороты.Субконто1 = &ИскомыйКонтрагент Значение этого параметра вы можете задать по умолчанию прямо в настройках схемы компоновки данных или предоставить возможность его выбора конечному пользователю при формировании отчета. Это сохранит универсальность вашего отчета.
  2. Использование вычисляемых полей и механизмов отбора. СКД позволяет перенести логику условных переходов из текста самого SQL-запроса на уровень процессора компоновки данных. Вы можете создать «Вычисляемое поле» в схеме компоновки данных, прописать для него выражение вычисления на языке выражений СКД и использовать стандартные отборы. Это разгрузит сервер базы данных и сделает структуру отчета понятной для дальнейшего сопровождения.
← На главную