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