В практике разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда один и тот же параметр запроса может принимать значения разных типов: это может быть ссылка на конкретный документ, значение Неопределено или NULL. Типовая проблема заключается в том, что механизм запросов 1С не всегда может корректно сопоставить тип параметра внутри оператора ВЫБОР или при использовании конструкции ССЫЛКА. В этой статье мы подробно разберем, почему возникают такие ошибки и как их эффективно обойти.
Рассмотрим ситуацию, когда мы передаем в запрос параметр &ДокументВозврата. Он может быть заполнен ссылкой на Документ.ВозвратСырьяОтПереработчика, ссылкой на Документ.ПоступлениеТоваровОтХранителя или быть пустым (Неопределено). Проблема возникает в конструкции следующего вида:
ГДЕ
ВЫБОР
КОГДА &ДокументВозврата ССЫЛКА Документ.ВозвратСырьяОтПереработчика
ТОГДА ДвиженияСерийТоваров.Документ = &ДокументВозврата
ИНАЧЕ ИСТИНА
КОНЕЦ
Проанализируем причины сбоя. Оператор ССЫЛКА предназначен для проверки типа поля в базе данных. Когда мы применяем его к параметру, система пытается определить тип параметра еще на этапе компиляции запроса. Если в параметр передается NULL или Неопределено, SQL-планировщик не может заранее сопоставить это значение с конкретной таблицей. Это часто приводит к тому, что условие просто игнорируется или вызывает ошибку несовместимости типов в сложных соединениях.
Наиболее надежным и производительным способом, как выяснилось в ходе обсуждения, является модификация текста запроса непосредственно в коде встроенного языка перед его выполнением. Рассмотрим, как реализовать этот метод через замену служебных комментариев.
Сначала подготовим шаблон запроса, вставив в него маркер (комментарий):
Запрос.Текст =
"ВЫБРАТЬ
| ...
|ИЗ
| РегистрНакопления.ДвиженияСерийТоваров КАК ДвиженияСерийТоваров
|ГДЕ
| ДвиженияСерийТоваров.СкладскаяОперация = ЗНАЧЕНИЕ(Перечисление.СкладскиеОперации.Приемка)
| // ОТБОР_ПО_ДОКУМЕНТУ";
Затем проанализируем состояние переменной и, если отбор необходим, заменим комментарий на реальное условие:
Если ЗначениеЗаполнено(ПеремещениеОтборДокументВозврата) Тогда
Запрос.Текст = СтрЗаменить(Запрос.Текст, "// ОТБОР_ПО_ДОКУМЕНТУ",
" И ДвиженияСерийТоваров.Документ = &ДокументВозврата ");
Запрос.УстановитьПараметр("ДокументВозврата", ПеремещениеОтборДокументВозврата);
КонецЕсли;
Преимущества этого метода: мы не нагружаем СУБД лишними проверками на NULL. Если отбор не нужен, условие вообще не попадает в итоговый SQL-запрос, что позволяет оптимизатору использовать наиболее подходящие индексы (проблема SARGability).
Если мы хотим сохранить текст запроса статичным, можно использовать два параметра: один для самого значения отбора, второй — признак того, нужно ли этот отбор применять. Рассмотрим этот подход подробнее.
В запросе пропишем логическое условие ИЛИ:
ГДЕ
(&БезОграничений ИЛИ ДвиженияСерийТоваров.Документ = &ДокументВозврата)
В коде 1С подготовим параметры следующим образом:
Если НЕ ЗначениеЗаполнено(ПеремещениеОтборДокументВозврата) Тогда
Запрос.УстановитьПараметр("БезОграничений", Истина);
// Передаем любую пустую ссылку, чтобы не было ошибки типа
Запрос.УстановитьПараметр("ДокументВозврата", Документы.ВозвратСырьяОтПереработчика.ПустаяСсылка());
Иначе
Запрос.УстановитьПараметр("БезОграничений", Ложь);
Запрос.УстановитьПараметр("ДокументВозврата", ПеремещениеОтборДокументВозврата);
КонецЕсли;
Важный нюанс: обязательно используйте круглые скобки в условии запроса. Помните, что в языке запросов 1С Неопределено, NULL и ПустаяСсылка — это три разных сущности. Использование пустой ссылки конкретного типа (вместо NULL) помогает СУБД быстрее обрабатывать сравнение типов.
В некоторых случаях конструкция ССЫЛКА работает нестабильно с параметрами составного типа. Выясним причину: ССЫЛКА — это синтаксический сахар для проверки типа метаданных. Вместо этого попробуем использовать функцию ТИПЗНАЧЕНИЯ, которая работает на уровне типов данных СУБД.
Рассмотрим пример условия:
ГДЕ
ВЫБОР
КОГДА ТИПЗНАЧЕНИЯ(&ДокументВозврата) = ТИП(Документ.ВозвратСырьяОтПереработчика)
ТОГДА ДвиженияСерийТоваров.Документ = &ДокументВозврата
КОГДА ТИПЗНАЧЕНИЯ(&ДокументВозврата) = ТИП(Документ.ПоступлениеТоваровОтХранителя)
ТОГДА ДвиженияСерийТоваров.Документ = &ДокументВозврата
ИНАЧЕ ИСТИНА
КОНЕЦ
Этот метод более "чистый" с точки зрения логики запроса, однако он может работать медленнее на больших таблицах, так как заставляет СУБД вычислять тип для каждой строки. Поэтому для высоконагруженных систем рекомендуется возвращаться к Решению №1.
Неопределено часто интерпретируется как NULL, но при сравнении Поле = NULL результат всегда будет Ложь.ЗначениеЗаполнено(). Она корректно обрабатывает и NULL, и Неопределено, и пустые ссылки.ВЫБОР лучше передавать в параметр Массив. Даже если в массиве один элемент, условие Документ В (&Массив) отработает корректно и быстро.Подводя итог, можно сказать: если вы столкнулись с тем, что запрос "не видит" отбор по документу или путает типы, самым эффективным методом будет динамическая подстановка условия через СтрЗаменить. Это гарантирует отсутствие конфликтов типов на уровне SQL и обеспечивает максимальную скорость выполнения запроса.