Как решить проблему сопоставления типов (NULL, Неопределено и Ссылка) в запросах 1С?

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

В практике разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда один и тот же параметр запроса может принимать значения разных типов: это может быть ссылка на конкретный документ, значение Неопределено или NULL. Типовая проблема заключается в том, что механизм запросов 1С не всегда может корректно сопоставить тип параметра внутри оператора ВЫБОР или при использовании конструкции ССЫЛКА. В этой статье мы подробно разберем, почему возникают такие ошибки и как их эффективно обойти.

Суть проблемы: почему стандартные условия не срабатывают?

Рассмотрим ситуацию, когда мы передаем в запрос параметр &ДокументВозврата. Он может быть заполнен ссылкой на Документ.ВозвратСырьяОтПереработчика, ссылкой на Документ.ПоступлениеТоваровОтХранителя или быть пустым (Неопределено). Проблема возникает в конструкции следующего вида:


ГДЕ
    ВЫБОР
        КОГДА &ДокументВозврата ССЫЛКА Документ.ВозвратСырьяОтПереработчика
            ТОГДА ДвиженияСерийТоваров.Документ = &ДокументВозврата
        ИНАЧЕ ИСТИНА
    КОНЕЦ

Проанализируем причины сбоя. Оператор ССЫЛКА предназначен для проверки типа поля в базе данных. Когда мы применяем его к параметру, система пытается определить тип параметра еще на этапе компиляции запроса. Если в параметр передается NULL или Неопределено, SQL-планировщик не может заранее сопоставить это значение с конкретной таблицей. Это часто приводит к тому, что условие просто игнорируется или вызывает ошибку несовместимости типов в сложных соединениях.

Решение №1: Динамическое формирование текста запроса

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

Сначала подготовим шаблон запроса, вставив в него маркер (комментарий):


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

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


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

Преимущества этого метода: мы не нагружаем СУБД лишними проверками на NULL. Если отбор не нужен, условие вообще не попадает в итоговый SQL-запрос, что позволяет оптимизатору использовать наиболее подходящие индексы (проблема SARGability).

Решение №2: Использование дополнительного булева флага

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

В запросе пропишем логическое условие ИЛИ:


ГДЕ
    (&БезОграничений ИЛИ ДвиженияСерийТоваров.Документ = &ДокументВозврата)

В коде 1С подготовим параметры следующим образом:


Если НЕ ЗначениеЗаполнено(ПеремещениеОтборДокументВозврата) Тогда
    Запрос.УстановитьПараметр("БезОграничений", Истина);
    // Передаем любую пустую ссылку, чтобы не было ошибки типа
    Запрос.УстановитьПараметр("ДокументВозврата", Документы.ВозвратСырьяОтПереработчика.ПустаяСсылка());
Иначе
    Запрос.УстановитьПараметр("БезОграничений", Ложь);
    Запрос.УстановитьПараметр("ДокументВозврата", ПеремещениеОтборДокументВозврата);
КонецЕсли;

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

Решение №3: Проверка типа через ТИПЗНАЧЕНИЯ

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

Рассмотрим пример условия:


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

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

Рекомендации по работе с пустыми значениями

  1. Не путайте NULL и Неопределено. В параметрах запроса Неопределено часто интерпретируется как NULL, но при сравнении Поле = NULL результат всегда будет Ложь.
  2. Используйте ЗначениеЗаполнено. Перед установкой параметра в коде всегда проверяйте его функцией ЗначениеЗаполнено(). Она корректно обрабатывает и NULL, и Неопределено, и пустые ссылки.
  3. Параметр-массив. Если типов документов много, вместо длинных условий ВЫБОР лучше передавать в параметр Массив. Даже если в массиве один элемент, условие Документ В (&Массив) отработает корректно и быстро.

Подводя итог, можно сказать: если вы столкнулись с тем, что запрос "не видит" отбор по документу или путает типы, самым эффективным методом будет динамическая подстановка условия через СтрЗаменить. Это гарантирует отсутствие конфликтов типов на уровне SQL и обеспечивает максимальную скорость выполнения запроса.

← На главную