Почему сравнение «не равно» в СКД не работает с пустыми полями и как это исправить

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

При разработке отчетов в Системе компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда фильтр «равно» работает ожидаемо, а фильтр «не равно» (<>) выдает пустой результат или некорректные данные. Особенно часто это проявляется при сравнении полей, полученных из разных источников — например, из регистра сведений и регистра накопления. Давайте разберем по шагам, почему это происходит и как правильно организовать сравнение значений в 1С.

Причина проблемы: Трехзначная логика (3VL) и NULL

Главная причина кроется в особенностях работы СУБД с типом NULL. В языке запросов 1С, как и в стандартном SQL, используется трехзначная логика. Результатом сравнения любого значения с NULL (будь то =, <>, > или <) всегда будет не Истина и не Ложь, а Неизвестно (UNKNOWN).

Рассмотрим ситуацию: у нас есть Поле1 со значением пустой ссылки перечисления и Поле2, которое в результате левого соединения приняло значение NULL. Нам кажется, что они не равны, но для системы условие Поле1 <> Поле2 превращается в ПустаяСсылка <> NULL, что дает Неизвестно. Поскольку СКД выводит только те строки, где условие фильтра дает Истина, такие строки просто отсекаются из отчета.

Различие между NULL, Пустой ссылкой и Неопределено

Для правильного решения задачи нам необходимо четко различать эти понятия, так как методы их обработки в запросах различаются:

  1. Пустая ссылка — это реальное значение типа «Ссылка». Оно физически записано в базе данных (состоит из нулевого GUID). Появляется, когда объект существует, но ссылка на него не выбрана.
  2. NULL — это отсутствие данных как таковых. В 1С NULL чаще всего возникает при использовании ЛЕВОГО СОЕДИНЕНИЯ, когда во второй таблице не нашлось соответствующей записи.
  3. Неопределено — специфическое значение, которое принимают поля составного типа, если значение в них не задано, но запись в базе существует.

Проанализируем ситуацию: если мы обращаемся к реквизиту объекта через точку (например, Продажи.Номенклатура.ТипНоменклатуры), и у номенклатуры тип не заполнен, мы получим Пустую ссылку (для простого типа) или Неопределено (для составного типа). Но если мы соединяем таблицу Продажи с таблицей Цены по номенклатуре, и цены для какой-то позиции нет — в поле цены мы получим NULL.

Решение 1: Обработка NULL в тексте запроса

Самый надежный способ — привести NULL к конкретному значению (пустой ссылке нужного типа) еще на уровне формирования набора данных. Рассмотрим пример кода, который следует использовать в конструкторе запроса СКД:


ВЫБРАТЬ
    РегистрСведений.Поле1 КАК Поле1,
    ЕСТЬNULL(РегистрНакопления.Поле2, ЗНАЧЕНИЕ(Перечисление.ТипПеречисления.ПустаяСсылка)) КАК Поле2
ИЗ
    РегистрСведений КАК РегистрСведений
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления КАК РегистрНакопления
    ПО РегистрСведений.Ключ = РегистрНакопления.Ключ

Функция ЕСТЬNULL проверяет первый аргумент. Если он равен NULL, она подставляет второй аргумент. Таким образом, в СКД будут попадать только типизированные значения, и стандартные операторы сравнения (=, <>) начнут работать корректно.

Решение 2: Обработка составных типов данных

Если поле, которое вы сравниваете, имеет составной тип, подстановка конкретной пустой ссылки перечисления может быть неуместна. В этом случае правильнее использовать значение Неопределено. Разберем, как это выглядит в запросе:


ВЫБРАТЬ
    ЕСТЬNULL(Источник.СоставноеПоле, НЕОПРЕДЕЛЕНО) КАК СоставноеПоле
ИЗ
    Справочник.Номенклатура КАК Источник

Это позволит избежать ошибок при сравнении, так как Неопределено является конкретным значением, с которым платформа 1С умеет работать в рамках логических операций, в отличие от NULL.

Решение 3: Логические условия в настройках фильтрации

Иногда нет возможности менять запрос (например, в типовых отчетах). В таком случае можно изменить само условие сравнения. Вместо простого Поле1 <> Поле2, мы можем проанализировать ситуацию более глубоко. Посмотрим на логическую конструкцию, которая будет корректно обрабатывать NULL:

Для проверки неравенства:
(НЕ Поле1 = Поле2) ИЛИ (Поле1 ЕСТЬ NULL И Поле2 ЕСТЬ НЕ NULL) ИЛИ (Поле1 ЕСТЬ НЕ NULL И Поле2 ЕСТЬ NULL)

Эта конструкция явно учитывает случаи, когда одно из значений отсутствует. Однако, следует помнить, что в СКД на закладке «Настройки» — «Отбор» такие сложные выражения прописать сложно. Поэтому использование ЕСТЬNULL в вычисляемых полях или в самом запросе остается приоритетным вариантом.

Практические советы по работе в конструкторе СКД

Рассмотрим, где лучше всего вносить изменения. У нас есть два пути:

  1. Вкладка «Наборы данных»: Изменяем текст запроса, добавляя ЕСТЬNULL. Это самый производительный метод, так как преобразование происходит на стороне СУБД.
  2. Вкладка «Вычисляемые поля»: Если вы не хотите менять основной запрос, создайте вычисляемое поле с выражением: ЕстьNull(Поле1, Значение(Перечисление.Тип.ПустаяСсылка)). СКД применит это выражение при расчете результата.

Проанализируем важный нюанс: если у вас много полей (как упоминал автор темы — полдюжины), не обязательно описывать каждое через ЕСТЬNULL. Посмотрите, нельзя ли изменить тип соединения в запросе или убедиться, что данные в регистрах инициализированы (записаны пустые ссылки вместо отсутствия записей). Однако в 90% случаев при ЛЕВОМ СОЕДИНЕНИИ явная обработка NULL является обязательным стандартом разработки.

Итоги

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

Используя эти методы, вы обеспечите точность данных в ваших отчетах и избежите трудноуловимых ошибок при фильтрации данных.

← На главную