При написании запросов к базе данных 1С часто возникает задача отфильтровать записи по признаку заполненности определенного поля. Если для строк или чисел проверка очевидна, то при работе со ссылочными типами данных, в частности с перечислениями, начинающие разработчики нередко сталкиваются с затруднениями: как проверить, выбрано ли значение, не перебирая при этом все возможные элементы перечисления?
Разберем подробно все правильные способы проверки заполнения перечислений, проанализируем ошибки и выясним, как платформа 1С обрабатывает такие условия на уровне СУБД.
Самым правильным, лаконичным и быстродействующим способом проверки реквизита ссылочного типа на незаполненность является сравнение с пустой ссылкой через литерал ЗНАЧЕНИЕ прямо в тексте запроса.
Посмотрим на пример запроса, в котором отбираются документы с незаполненным перечислением:
ВЫБРАТЬ
ВнутренниеДокументы.Ссылка КАК Ссылка
ИЗ
Справочник.ВнутренниеДокументы КАК ВнутренниеДокументы
ГДЕ
ВнутренниеДокументы.IIT_ОтказОтЗаключенияДоговора = ЗНАЧЕНИЕ(Перечисление.IIT_ОтказОтЗаключенияДоговора.ПустаяСсылка)
Обратим внимание на синтаксис: после имени перечисления указывается ключевое слово ПустаяСсылка без круглых скобок. В языке запросов 1С допустима также сокращенная форма записи — достаточно поставить точку после имени перечисления:
ГДЕ
ВнутренниеДокументы.IIT_ОтказОтЗаключенияДоговора = ЗНАЧЕНИЕ(Перечисление.IIT_ОтказОтЗаключенияДоговора.)
Если же перед нами стоит противоположная задача — отобрать только те строки, где значение перечисления заполнено, мы используем оператор сравнения <>:
ГДЕ
ВнутренниеДокументы.IIT_ОтказОтЗаключенияДоговора <> ЗНАЧЕНИЕ(Перечисление.IIT_ОтказОтЗаключенияДоговора.ПустаяСсылка)
Иногда в коде можно встретить конструкцию вида ГДЕ Таблица.Реквизит.Ссылка ЕСТЬ NULL. Рассмотрим, почему такой подход считается неоптимальным:
Реквизит.Ссылка) заставляет платформу сгенерировать дополнительное левое соединение (LEFT JOIN) с таблицей перечисления на уровне SQL.NULL.Проанализируем, как пустая ссылка хранится физически. В базе данных пустая ссылка любого ссылочного типа (включая перечисления) представляет собой последовательность из 16 нулевых байтов (0x00000000000000000000000000000000).
Когда мы пишем ЗНАЧЕНИЕ(Перечисление.Имя.ПустаяСсылка), платформа 1С подставляет в итоговый SQL-запрос прямое сравнение поля с бинарным нулем. Если по реквизиту построен индекс, СУБД мгновенно найдет нужные записи по индексу без лишних затрат ресурсов.
Важно помнить о ситуациях, когда проверяемый реквизит находится в таблице, присоединенной через ЛЕВОЕ СОЕДИНЕНИЕ. Если строка в правой таблице не найдена, значение реквизита будет равно NULL, а не пустой ссылке.
Так как в SQL любое прямое сравнение с NULL возвращает ложь, условие Реквизит = ЗНАЧЕНИЕ(...) проигнорирует ненайденные строки. Чтобы учесть оба случая (и когда значение не заполнено, и когда самой записи нет), используем функцию ЕСТЬNULL:
ГДЕ
ЕСТЬNULL(Таблица.Реквизит, ЗНАЧЕНИЕ(Перечисление.Имя.ПустаяСсылка)) = ЗНАЧЕНИЕ(Перечисление.Имя.ПустаяСсылка)
Либо явно перечисляем условия через оператор ИЛИ:
ГДЕ
Таблица.Реквизит ЕСТЬ NULL
ИЛИ Таблица.Реквизит = ЗНАЧЕНИЕ(Перечисление.Имя.ПустаяСсылка)
Если реквизит имеет составной тип данных (например, может принимать значение перечисления, справочника или быть неопределенным), простое сравнение с пустой ссылкой перечисления сработает только тогда, когда в реквизит записан именно данный тип.
В таких случаях разберем следующие правила:
ГДЕ
Таблица.Реквизит ССЫЛКА Перечисление.Имя
И Таблица.Реквизит <> ЗНАЧЕНИЕ(Перечисление.Имя.ПустаяСсылка)
ВЫРАЗИТЬ(Таблица.Реквизит КАК Перечисление.Имя).