Как исправить ошибку сравнения данных из виртуальной таблицы и избавиться от NULL в 1С

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

При разработке отчетов и внешних обработок разработчики часто сталкиваются с ситуацией, когда данные из виртуальных таблиц регистров сведений, накопления или бухгалтерии при проверке в коде вызывают исключение. Выясним причину такого поведения платформы и подробно рассмотрим варианты корректной обработки числовых показателей.

Анализ проблемы: почему возникает ошибка сравнения

Разберем типичную ситуацию: в коде модуля выполняется обход результата запроса в цикле с проверкой числовых условий:


Для Каждого ОС Из Запрос1 Цикл
    Если ОС.Амортизация = 0 И (ОС.КолЗолото <> 0 ИЛИ ОС.КолСеребро <> 0) Тогда
        // Обработка строки
    КонецЕсли;
КонецЦикла;

При выполнении такого кода платформа может выбросить исключительную ситуацию: «Операции сравнения на больше-меньше допустимы только для значений совпадающих примитивных типов (Булево, Число, Строка, Дата)».

Проанализируем механику возникновения ошибки. В запросе используется ЛЕВОЕ СОЕДИНЕНИЕ справочника основных средств с виртуальной таблицей СтоимостьОСБухгалтерскийУчетОстаткиИОбороты. Если по объекту учета за выбранный период не было оборотов амортизации или остатков, в результирующей выборке поле АмортизацияОборот принимает системное значение NULL, а не числовое значение 0.

Операторы сравнения на равенство (=) или неравенство (<>) во встроенном языке 1С могут работать с типом NULL, однако если в выражении встречается проверка на больше или меньше, либо поля сравниваются с числами в сложных логических блоках, BSL требует строгого совпадения примитивных типов.

Способ 1. Замена NULL на 0 в тексте запроса с помощью ЕСТЬNULL

Рассмотрим основной и наиболее рекомендуемый способ решения проблемы — предварительную обработку пустых значений непосредственно на уровне запроса СУБД. Для этого используем встроенную функцию языка запросов ЕСТЬNULL.

Заменим прямую выборку поля на конструкцию с подстановкой значения по умолчанию:


ВЫБРАТЬ
    ОсновныеСредства.Ссылка КАК ОсновноеСредство,
    ЕСТЬNULL(СтоимостьОСБухгалтерскийУчетОстаткиИОбороты.АмортизацияОборот, 0) КАК Амортизация,
    ЕСТЬNULL(Драгметаллы.КолЗолото, 0) КАК КолЗолото,
    ЕСТЬNULL(Драгметаллы.КолСеребро, 0) КАК КолСеребро
ИЗ
    Справочник.ОсновныеСредства КАК ОсновныеСредства
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(&ПериодНачала, &ПериодКонца, ...) КАК СтоимостьОСБухгалтерскийУчетОстаткиИОбороты
        ПО ОсновныеСредства.Ссылка = СтоимостьОСБухгалтерскийУчетОстаткиИОбороты.Субконто1

Функция ЕСТЬNULL(Поле, 0) гарантирует, что в выборку попадет значение типа Число. Это полностью исключает возникновение ошибок при дальнейших математических вычислениях и проверках в цикле.

Способ 2. Перенос условий отбора в секцию ГДЕ запроса

Если задача заключается в том, чтобы вовсе не обрабатывать основные средства с нулевой или отсутствующей амортизацией, перенесем логику фильтрации из цикла модуля в сам запрос. Это снизит нагрузку на оперативную память и ускорит выполнение программы.

Разберем по шагам, как отфильтровать строки на уровне СУБД:

  1. Исключение пустых записей по условию: добавим в секцию ГДЕ фильтр по обороту амортизации:
    
    ГДЕ
        ЕСТЬNULL(СтоимостьОСБухгалтерскийУчетОстаткиИОбороты.АмортизацияОборот, 0) <> 0
    
  2. Использование внутреннего соединения: если необходимо получать данные только по тем объектам, по которым есть движения в регистре, заменим ЛЕВОЕ СОЕДИНЕНИЕ на ВНУТРЕННЕЕ СОЕДИНЕНИЕ. При внутреннем соединении строки без движений отсекаются автоматически, а поля регистра гарантированно содержат числовые значения без NULL.

Способ 3. Вычисление логических флагов в запросе

Чтобы не перегружать код длинными цепочками условий вида ОС.КолЗолото <> 0 ИЛИ ОС.КолСеребро <> 0, вынесем логическую проверку драгметаллов в поле выборки через оператор ВЫБОР:


ВЫБРАТЬ
    ОсновныеСредства.Ссылка КАК ОсновноеСредство,
    ЕСТЬNULL(СтоимостьОСБухгалтерскийУчетОстаткиИОбороты.АмортизацияОборот, 0) КАК Амортизация,
    ВЫБОР
        КОГДА ЕСТЬNULL(Драгметаллы.КолЗолото, 0) <> 0
            ИЛИ ЕСТЬNULL(Драгметаллы.КолСеребро, 0) <> 0
            ИЛИ ЕСТЬNULL(Драгметаллы.КолПлатина, 0) <> 0
        ТОГДА Истина
        ИНАЧЕ Ложь
    КОНЕЦ КАК ЕстьДрагМеталлы
ИЗ
    Справочник.ОсновныеСредства КАК ОсновныеСредства
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(...) КАК СтоимостьОСБухгалтерскийУчетОстаткиИОбороты
        ПО ОсновныеСредства.Ссылка = СтоимостьОСБухгалтерскийУчетОстаткиИОбороты.Субконто1

Теперь в модуле код проверки станет лаконичным и безопасным:


Для Каждого ОС Из Выборка Цикл
    Если ОС.Амортизация = 0 И ОС.ЕстьДрагМеталлы Тогда
        // Выполняем действия для основных средств с нулевой амортизацией и драгметаллами
    КонецЕсли;
КонецЦикла;

Способ 4. Проверка и нормализация данных во встроенном языке (BSL)

Если текст запроса изменить невозможно (например, данные получены из закрытого метода типовой конфигурации), выполним нормализацию значений перед проверкой в коде.

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


Для Каждого ОС Из РезультатЗапроса Цикл
    ТекущаяАмортизация = ?(ОС.Амортизация = NULL, 0, ОС.Амортизация);
    
    Если ТекущаяАмортизация = 0 Тогда
        // Безопасное выполнение условий
    КонецЕсли;
КонецЦикла;

Также для проверки типа можно использовать прямое условие ТипЗнч(ОС.Амортизация) = Тип("Null"), что позволяет обработать неинициализированные значения до вызова логических операций.

← На главную