В практике разработчика 1С иногда возникают ситуации, которые кажутся мистическими: один и тот же запрос в консоли запросов выдает нужные данные, а при выполнении в программном коде — возвращает пустую выборку. Если заглянуть «под капот» и проанализировать выполнение запроса на стороне СУБД с помощью профайлера, можно обнаружить странную конструкцию WHERE (1=0). Давайте детально разберем, почему платформа 1С ведет себя подобным образом, как работает внутренний оптимизатор и как избежать подобных ошибок.
Рассмотрим типичную ситуацию. Программист выполняет запрос к табличной части документа, пытаясь получить данные с итогами. Однако результат оказывается пустым. При изучении текста SQL-запроса, который платформа отправляет на сервер (например, MS SQL Server), мы видим следующую картину:
SELECT
T1._Document25_IDRRef,
T2._Fld476RRef,
T2._Date_Time
FROM dbo._Document25_VT3180 T1 WITH(NOLOCK)
LEFT OUTER JOIN dbo._Document25 T2 WITH(NOLOCK)
ON T1._Document25_IDRRef = T2._IDRRef
WHERE (1=0) AND (T2._Date_Time >= P1)
Условие WHERE (1=0) является заведомо ложным. Это означает, что СУБД даже не будет пытаться искать данные в таблицах, так как результат всего выражения всегда будет «Ложь». Платформа 1С делает это намеренно. Проанализируем причину такого поведения.
Система 1С:Предприятие обладает механизмом предварительного анализа (трансляции) запроса. Перед тем как отправить запрос в базу данных, платформа проверяет типы переданных параметров. Если тип параметра, указанного в условии ГДЕ, не совпадает с типом поля в метаданных (и не может быть к нему приведен), оптимизатор понимает: совпадений быть не может физически.
Разберем пример кода, который приводит к такой ошибке:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказТоваров.Ссылка КАК Ссылка,
| ЗаказТоваров.Издательство КАК Издательство
|ИЗ
| Документ.ЗаказТоваров.ДокументыИздательств КАК ЗаказТоваров
|ГДЕ
| ЗаказТоваров.Издательство = &Издательство";
// Ошибка здесь: в параметр передается ссылка не того типа
// Например, СправочникСсылка.Контрагенты вместо СправочникСсылка.Организации
Запрос.УстановитьПараметр("Издательство", ВыбранныйОбъект);
Результат = Запрос.Выполнить();
Если поле Издательство в метаданных имеет тип СправочникСсылка.Издательства, а в переменную ВыбранныйОбъект попала ссылка на справочник Контрагенты, транслятор запросов 1С сразу подставит 1=0. Это экономит ресурсы сервера, предотвращая выполнение заведомо бессмысленного поиска.
Выясним причину, по которой консоль запросов часто вводит разработчика в заблуждение. В консоли мы устанавливаем параметры интерактивно. Когда мы нажимаем на выбор значения параметра, консоль «подсматривает» в метаданные и предлагает выбрать объект именно того типа, который ожидает поле. В результате в параметр всегда попадает верный тип данных.
В программном же коде типизация динамическая. Мы можем по ошибке передать в УстановитьПараметр() строку вместо ссылки, или ссылку другого типа. Именно это различие в способе заполнения параметров и создает иллюзию «странного» поведения платформы.
Рассмотрим подробнее, в каких еще случаях платформа может добавить условие 1=0 или 0=1:
ГДЕ Истина = Ложь или сравнение констант разных типов также приведет к оптимизации.Неопределено в параметр поля, которое не является составным и не допускает такой тип, провоцирует аналогичную реакцию.Проанализируем шаги, которые помогут быстро найти ошибку, если ваш запрос возвращает пустой результат:
Запрос.Выполнить() установите точку останова. Используйте «Вычислить выражение» (Shift+F9) для проверки ТипЗнч(Параметр). Сравните его с типом поля в конфигураторе.Важный совет: Если вы работаете с составными типами, платформа при генерации SQL-запроса добавляет проверку по полю _TYPE. Если передаваемый тип параметра валиден для этого поля, но данных по нему нет, условие 1=0 не появится, и СУБД выполнит честный поиск. Но если тип «чужой» — сработает оптимизатор.
Посмотрим на пример безопасной передачи параметров, исключающей подстановку 1=0 из-за неверного типа:
Если ЗначениеЗаполнено(ВыбранныйОбъект)
И ТипЗнч(ВыбранныйОбъект) = Тип("СправочникСсылка.Издательства") Тогда
Запрос.УстановитьПараметр("Издательство", ВыбранныйОбъект);
Иначе
// Обработка ошибки или установка пустой ссылки нужного типа
Запрос.УстановитьПараметр("Издательство", Справочники.Издательства.ПустаяСсылка());
КонецЕсли;
Такой подход гарантирует, что в SQL-запрос уйдет корректный тип данных, и платформа не будет вынуждена использовать жесткую оптимизацию через WHERE 1=0. Использование этого механизма платформой 1С — это не ошибка, а высокоэффективный способ снижения нагрузки на сервер базы данных, исключающий бесполезные операции сканирования таблиц.