Как правильно обрабатывать NULL при возврате максимального значения в запросах 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Анализ проблемы: почему ЕСТЬNULL внутри МАКСИМУМ не работает

Рассмотрим распространенную ситуацию: разработчику необходимо написать запрос для получения максимального номера паллеты из справочника, а в случае отсутствия записей вернуть 0. На первый взгляд кажется логичным обернуть проверяемый реквизит в функцию ЕСТЬNULL непосредственно внутри агрегата МАКСИМУМ:


ВЫБРАТЬ
    МАКСИМУМ(ЕСТЬNULL(яШтрихКодыПаллет.НомерПаллеты, 0)) КАК НомерПаллеты
ИЗ
    Справочник.яШтрихКодыПаллет КАК яШтрихКодыПаллет
ГДЕ
    яШтрихКодыПаллет.Дата = &Дата
    И яШтрихКодыПаллет.НомерСмены = &НомерСмены
    И яШтрихКодыПаллет.Владелец = &Владелец

Однако при выполнении такого запроса, если под условия в секции ГДЕ не подпадает ни одна запись, результат все равно возвращает значение NULL.

Выясним причину такого поведения СУБД и платформы. Порядок обработки функций в запросе следующий: функция ЕСТЬNULL проверяет значения в каждой конкретной строке таблицы до выполнения агрегации. Если условия отбора не отфильтровали ни одной записи, исходная выборка пуста, и внутренняя функция ЕСТЬNULL не вызывается ни разу.

В свою очередь, агрегатная функция МАКСИМУМ (без секции СГРУППИРОВАТЬ ПО), примененная к пустой выборке, по стандартам SQL и платформы 1С всегда возвращает одну строку со значением NULL. При этом метод РезультатЗапроса.Пустой() вернет значение Ложь, так как итоговая выборка фактически содержит одну строку с результатом NULL.

Решение 1. Использование ЕСТЬNULL поверх функции МАКСИМУМ (Рекомендуемый вариант)

Чтобы корректно обработать отсутствие записей на уровне базы данных, необходимо изменить порядок вызова функций: обернуть сам результат работы агрегатной функции МАКСИМУМ в функцию ЕСТЬNULL.

Разберем верный вариант написания запроса:


ВЫБРАТЬ
    ЕСТЬNULL(МАКСИМУМ(яШтрихКодыПаллет.НомерПаллеты), 0) КАК НомерПаллеты
ИЗ
    Справочник.яШтрихКодыПаллет КАК яШтрихКодыПаллет
ГДЕ
    яШтрихКодыПаллет.Дата = &Дата
    И яШтрихКодыПаллет.НомерСмены = &НомерСмены
    И яШтрихКодыПаллет.Владелец = &Владелец

В этом случае при отсутствии записей функция МАКСИМУМ возвращает NULL, после чего внешняя функция ЕСТЬNULL перехватывает его и заменяет на указанное значение — 0. Этот подход является самым оптимальным по производительности и стандартным при разработке конфигураций и сдаче экзаменов «1С:Специалист».

Решение 2. Использование конструкции ВЫБОР КОГДА

Если требуется применить более развернутое условие или явно описать логику проверки в запросе, можно использовать конструкцию ВЫБОР КОГДА:


ВЫБРАТЬ
    ВЫБОР
        КОГДА МАКСИМУМ(яШтрихКодыПаллет.НомерПаллеты) ЕСТЬ NULL
            ТОГДА 0
        ИНАЧЕ МАКСИМУМ(яШтрихКодыПаллет.НомерПаллеты)
    КОНЕЦ КАК НомерПаллеты
ИЗ
    Справочник.яШтрихКодыПаллет КАК яШтрихКодыПаллет
ГДЕ
    яШтрихКодыПаллет.Дата = &Дата
    И яШтрихКодыПаллет.НомерСмены = &НомерСмены
    И яШтрихКодыПаллет.Владелец = &Владелец

Обратите внимание: проверка на отсутствие значения в языке запросов выполняется только с помощью оператора ЕСТЬ NULL. Прямое сравнение со знаком равенства (= NULL) вернет неопределенный результат, так как любые операции сравнения с NULL дают неизвестное значение.

Решение 3. Выборка первой записи с сортировкой

Альтернативный подход заключается в отказе от использования агрегатных функций. Для получения максимального значения можно отсортировать выборку по убыванию и ограничить количество записей с помощью ключевого слова ПЕРВЫЕ 1:


ВЫБРАТЬ ПЕРВЫЕ 1
    яШтрихКодыПаллет.НомерПаллеты КАК НомерПаллеты
ИЗ
    Справочник.яШтрихКодыПаллет КАК яШтрихКодыПаллет
ГДЕ
    яШтрихКодыПаллет.Дата = &Дата
    И яШтрихКодыПаллет.НомерСмены = &НомерСмены
    И яШтрихКодыПаллет.Владелец = &Владелец
УПОРЯДОЧИТЬ ПО
    яШтрихКодыПаллет.НомерПаллеты УБЫВ

Главные особенности этого подхода:

  1. Если записи под условия отбора не подпадают, запрос вернет 0 строк (полностью пустой результат).
  2. Метод РезультатЗапроса.Пустой() в этом случае вернет Истина, что удобно проверять во встроенном языке 1С.

Важное замечание по производительности: вариант с ПЕРВЫЕ 1 и УПОРЯДОЧИТЬ ПО эффективен только при наличии составного индекса в базе данных по полям фильтрации и полю сортировки. В противном случае СУБД выполнит сканирование всей таблицы с сортировкой в памяти.

Решение 4. Обработка результата во встроенном языке 1С

Если в самом запросе обработка NULL не выполнялась, подстраховать получение числового значения можно на уровне встроенного языка 1С. Попытка выполнить математические операции с объектом типа Null вызовет ошибку платформы «Преобразование к типу число не может быть выполнено».

Проанализируем пример функции во встроенном языке с использованием системной функции ЗначениеЗаполнено:


Функция ПолучитьМаксимальныйНомерПаллеты(Дата, НомерСмены, Владелец) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	МАКСИМУМ(яШтрихКодыПаллет.НомерПаллеты) КАК НомерПаллеты
        |ИЗ
        |	Справочник.яШтрихКодыПаллет КАК яШтрихКодыПаллет
        |ГДЕ
        |	яШтрихКодыПаллет.Дата = &Дата
        |	И яШтрихКодыПаллет.НомерСмены = &НомерСмены
        |	И яШтрихКодыПаллет.Владелец = &Владелец";

    Запрос.УстановитьПараметр("Дата", Дата);
    Запрос.УстановитьПараметр("НомерСмены", НомерСмены);
    Запрос.УстановитьПараметр("Владелец", Владелец);

    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат ?(ЗначениеЗаполнено(Выборка.НомерПаллеты), Выборка.НомерПаллеты, 0);
    КонецЕсли;

    Возврат 0;

КонецФункции

Функция ЗначениеЗаполнено возвращает Ложь для значения типа Null, благодаря чему тернарный оператор ?() вернет безопасное числовое значение 0.

Итоги и рекомендации

Подведем итоги работы с агрегатными функциями и незаполненными значениями:

  1. Используйте синтаксис ЕСТЬNULL(МАКСИМУМ(Поле), 0) для получения гарантированного значения по умолчанию на уровне базы данных.
  2. Помните, что ЕСТЬNULL внутри агрегата обрабатывает поля отдельных строк, но не заменяет NULL, возникающий от пустой выборки.
  3. Во встроенном языке 1С для безопасной проверки полученного из выборки поля используйте функцию ЗначениеЗаполнено.
← На главную