При написании запросов в платформе 1С:Предприятие разработчики часто используют условный оператор ВЫБОР для ветвления логики. Однако пропуск необязательной ветки ИНАЧЕ может приводить к неожиданным ошибкам в прикладном коде, когда данные перестают попадать в результат выборки. Проанализируем, как работает конструкция ВЫБОР при отсутствии блока ИНАЧЕ, почему это ломает фильтрацию в секции ГДЕ и как правильно переписать такой запрос по стандартам разработки 1С.
Рассмотрим реальную ситуацию, с которой сталкиваются программисты после обновления конфигурации: перестает формироваться дерево связанных документов. В коде обнаружен следующий запрос к критерию отбора:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| СвязанныеДокументы.Ссылка КАК Ссылка
|ИЗ
| КритерийОтбора.СвязанныеДокументы(&ЗначениеКритерияОтбора) КАК СвязанныеДокументы
|ГДЕ
| ВЫБОР
| КОГДА СвязанныеДокументы.Ссылка ССЫЛКА Задача.ЗадачаИсполнителя
| ТОГДА СвязанныеДокументы.Ссылка.CRM_Личная
| ИЛИ СвязанныеДокументы.Ссылка.БизнесПроцесс ССЫЛКА БизнесПроцесс.CRM_БизнесПроцесс
| КОНЕЦ";
Разберем по шагам, что здесь происходит:
Ссылка в таблице КритерийОтбора.СвязанныеДокументы имеет составной ссылочный тип данных (документы разных видов, задачи, бизнес-процессы и др.).КОГДА СвязанныеДокументы.Ссылка ССЫЛКА Задача.ЗадачаИсполнителя возвращает Ложь.ИНАЧЕ в выражении отсутствует, срабатывает общее правило стандарта SQL: если ни одно из условий КОГДА не выполнилось и нет ветки ИНАЧЕ, оператор ВЫБОР (аналог SQL-конструкции CASE) возвращает значение NULL.ГДЕ действует трехзначная логика СУБД (True, False, Unknown). Чтобы строка попала в результирующую выборку, условие в блоке ГДЕ должно строго вычисляться в Истина. Значение NULL интерпретируется как неопределенность, поэтому все строки с обычными документами полностью отсекаются из итоговой таблицы.Таким образом, пропуск ИНАЧЕ не эквивалентен возврату Ложь на уровне типа данных, но в контексте секции ГДЕ результат оказывается аналогичным — строка отбрасывается.
Посмотрим на пример еще внимательнее и выясним причину потенциальных проблем с производительностью. В запросе выполняется обращение через точку к реквизитам Ссылка.CRM_Личная и Ссылка.БизнесПроцесс.
Поскольку поле Ссылка является составным, обращение к его реквизитам через точку без предварительного ограничения заставляет платформу формировать неявные соединения LEFT JOIN со всеми существующими таблицами объектов конфигурации, входящими в этот тип. Это существенно утяжеляет сформированный SQL-запрос.
Кроме того, использование конструкции ВЫБОР внутри секции ГДЕ нарушает принцип SARGability (Search Argument Able): оптимизатор СУБД теряет возможность применить поиск по индексам (Index Seek) и переходит к полному сканированию всей таблицы (Table Scan / Index Scan).
Если требуется быстро восстановить работоспособность выборки без кардинальной переработки структуры запроса, достаточно явно вернуть Истина для всех остальных типов ссылок, которые не являются задачами:
ГДЕ
ВЫБОР
КОГДА СвязанныеДокументы.Ссылка ССЫЛКА Задача.ЗадачаИсполнителя
ТОГДА СвязанныеДокументы.Ссылка.CRM_Личная
ИЛИ СвязанныеДокументы.Ссылка.БизнесПроцесс ССЫЛКА БизнесПроцесс.CRM_БизнесПроцесс
ИНАЧЕ ИСТИНА
КОНЕЦ
В этом случае строки, не относящиеся к типу Задача.ЗадачаИсполнителя, получают значение Истина и успешно возвращаются запросом.
Проанализируем рекомендуемый подход. Согласно стандартам разработки 1С, в условиях соединения ПО и условиях фильтрации ГДЕ следует избегать использования конструкции ВЫБОР, заменяя её стандартными булевыми операторами (И, ИЛИ, НЕ). При этом составные ссылочные поля перед обращением к их свойствам необходимо явно приводить с помощью функции ВЫРАЗИТЬ.
Перепишем блок условий в соответствии со стандартами платформы:
ГДЕ
НЕ СвязанныеДокументы.Ссылка ССЫЛКА Задача.ЗадачаИсполнителя
ИЛИ (ВЫРАЗИТЬ(СвязанныеДокументы.Ссылка КАК Задача.ЗадачаИсполнителя).CRM_Личная
ИЛИ ВЫРАЗИТЬ(СвязанныеДокументы.Ссылка КАК Задача.ЗадачаИсполнителя).БизнесПроцесс ССЫЛКА БизнесПроцесс.CRM_БизнесПроцесс)
Разберем преимущества этого варианта:
НЕ ... ССЫЛКА.ВЫРАЗИТЬ(Поле КАК Тип) предотвращает построение избыточных соединений со всеми таблицами метаданных, сужая соединение строго до таблицы Задача.ЗадачаИсполнителя.CASE для каждой отдельной строки.