Как правильно и быстро проверить тип регистратора в запросе: ССЫЛКА или ВЫРАЗИТЬ?

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

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

Вариант 1: Использование оператора ССЫЛКА

Этот вариант является классическим и наиболее рекомендуемым. Синтаксис выглядит следующим образом:


ГДЕ
    Взаимодействия.Регистратор ССЫЛКА Документ.CRM_ПредпринятоеДействие

Проанализируем, что происходит внутри системы при выполнении такого запроса. Поле Регистратор является составным типом данных. В физической таблице базы данных (например, MS SQL Server) это поле представлено несколькими колонками: идентификатором типа (обычно числовое поле) и самой ссылкой (GUID). Когда мы используем конструкцию ССЫЛКА, платформа 1С преобразует это в простейшее сравнение идентификатора типа в SQL-запросе.

Преимущества данного метода:

  1. SARGability (Search Argumentable): Это означает, что SQL-сервер может эффективно использовать индексы. Сравнение типа — это простая операция над числовым полем, которая позволяет быстро отсечь ненужные записи.
  2. Читабельность: Код понятен любому разработчику 1С и соответствует стандартам разработки.
  3. Надежность: Платформа корректно обрабатывает значения NULL и Неопределено в таких условиях, не вызывая аварийного завершения запроса.

Вариант 2: Использование конструкции ВЫРАЗИТЬ

Иногда разработчики пытаются применить «хитрый» способ проверки через приведение типа, аргументируя это (зачастую ошибочно) более высокой скоростью работы. Условие выглядит так:


ГДЕ
    НЕ ВЫРАЗИТЬ(Взаимодействия.Регистратор КАК Документ.CRM_ПредпринятоеДействие) ЕСТЬ NULL

Разберем по шагам логику этого метода. Платформа пытается привести значение Регистратор к конкретному типу. Если тип совпадает, возвращается ссылка, если нет — возвращается NULL. Затем условие проверяет, не является ли результат приведения пустым.

Выясним причину, почему этот метод может считаться менее эффективным. На уровне SQL-запроса ВЫРАЗИТЬ часто транслируется в конструкцию CASE WHEN... THEN... ELSE NULL END. Для выполнения такого условия СУБД вынуждена вычислять результат выражения для каждой строки таблицы. Это увеличивает нагрузку на процессор сервера и, в большинстве случаев, исключает эффективное использование индексов (происходит Index Scan вместо Index Seek).

Хотя в некоторых специфических сценариях (например, из-за особенностей кэширования плана запроса или устаревшей статистики) этот вариант может показать случайное преимущество в скорости на короткой дистанции, в долгосрочной перспективе на больших объемах данных он неизбежно проиграет.

Вариант 3: Использование функции ТИПЗНАЧЕНИЯ

Рассмотрим еще один возможный, но наименее эффективный вариант:


ГДЕ
    ТИПЗНАЧЕНИЯ(Регистратор) = ТИП(Документ.CRM_ПредпринятоеДействие)

Проанализируем ситуацию: функция ТИПЗНАЧЕНИЯ в запросе — это тяжелая операция. Она требует получения информации о типе объекта, что на уровне SQL превращается в еще более сложные конструкции, чем в случае с ВЫРАЗИТЬ. Замеры производительности показывают, что этот метод работает медленнее всех остальных и категорически не рекомендуется для использования в секции ГДЕ.

Проблема использования ВЫБОР в секции ГДЕ

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


ГДЕ
    ВЫБОР
        КОГДА &Фильтр = "ТипНоменклатуры"
            ТОГДА Номенклатура.ТипНоменклатуры = &ТипНоменклатуры
        КОГДА &Фильтр = "ВидНоменклатуры"
            ТОГДА Номенклатура.ВидНоменклатуры = &ВидНоменклатуры
        ИНАЧЕ ИСТИНА
    КОНЕЦ

Почему это работает медленно? Оптимизатор СУБД не всегда может заранее определить, какую ветку ВЫБОР нужно использовать, и строит универсальный (неоптимальный) план запроса. Чтобы решить эту проблему, рекомендуется использовать динамическое конструирование текста запроса на языке 1С.

Разберем решение этой задачи через подстановку параметров или текста:


ТекстЗапроса = "ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ ИСТИНА %Условие%";
Если ЗначениеЗаполнено(ТипНоменклатуры) Тогда
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "%Условие%", "И ТипНоменклатуры = &ТипНоменклатуры");
КонецЕсли;

Такой подход позволяет передать в СУБД максимально простой и прозрачный SQL-код, который будет гарантированно использовать нужные индексы.

Выводы и рекомендации

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

  1. Используйте ССЫЛКА: Это основной и самый быстрый способ фильтрации. Он корректно транслируется в SQL и эффективно работает с индексами базы данных.
  2. Избегайте ВЫРАЗИТЬ в условиях: Данная конструкция предназначена в первую очередь для списка выбора (секция ВЫБРАТЬ), чтобы ограничить количество соединений при обращении через точку к реквизитам составного типа. Использование её в ГДЕ избыточно и потенциально вредно для производительности.
  3. Откажитесь от сложных CASE в фильтрах: Если условия фильтрации зависят от параметров, лучше сформировать текст запроса динамически в коде 1С, чем заставлять СУБД вычислять логику внутри самого запроса.
  4. Помните о NULL: При работе с левыми соединениями всегда учитывайте, что поле типа может быть NULL. Оператор ССЫЛКА обрабатывает это безопаснее всего.

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

← На главную