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