Фильтр для NULL в списке - PullRequest
       10

Фильтр для NULL в списке

1 голос
/ 14 февраля 2011

Почему фильтр для NULL в подзапросах не работает? Я надеялся получить правильный результат, добавив NULL в список допустимых значений, например:

SELECT     ERP_ServiceProcess.fiStatusNew, RMA.IdRMA
FROM         ERP_ServiceProcess RIGHT OUTER JOIN
                      RMA ON ERP_ServiceProcess.fiRMA = RMA.IdRMA
WHERE  (ERP_ServiceProcess.fiStatusNew IN (NULL, 1, 7, 8))
order by ERP_ServiceProcess.fiStatusNew

Это дает неверный результат, потому что все записи в RMA, которые не имеют записей в под-таблице ERP_ServiceProcess (где ERP_ServiceProcess.fiStatusNew IS NULL) отброшены.

Я должен использовать этот (медленный) запрос, чтобы получить правильный результат:

SELECT     ERP_ServiceProcess.fiStatusNew, RMA.IdRMA
FROM         ERP_ServiceProcess RIGHT OUTER JOIN
                      RMA ON ERP_ServiceProcess.fiRMA = RMA.IdRMA
WHERE     (ERP_ServiceProcess.fiStatusNew IS NULL)
OR (ERP_ServiceProcess.fiStatusNew IN (1, 7, 8))
order by ERP_ServiceProcess.fiStatusNew

Почему я должен использовать второй, медленный запрос, хотя я использовал RIGHT OUTER JOIN, и я добавил NULL в подзапрос?

Заранее спасибо.

Ответы [ 3 ]

2 голосов
/ 14 февраля 2011

Это не работает так, как вы ожидаете, так как расширяется до множества операций равных

fiStatusNew = NULL OR fiStatusNew = 1 OR fiStatusNew = 7 OR fiStatusNew = 8

и anything = NULL неизвестно.

Учитывая это расширение, нет особой причины полагать, что добавление дополнительного OR с использованием IS NULL само по себе замедлит работу (дополнительный предикат может изменить план запроса на использование другого пути доступа, если статистика приведет его полагать, что число совпадающих строк оправдывает это)

Вы видите то же поведение в операции CASE

SELECT CASE NULL WHEN NULL THEN 'Yes' ELSE 'No' END /*Returns "No"*/

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

fiStatusNew NOT IN (NULL, 1,2)

Расширится до

fiStatusNew<> NULL and fiStatusNew<> 1 and fiStatusNew<> 2

или

Unknown And True/False/Unknown And True/False/Unknown

, который всегда оценивается как Unknown при трехзначной логике .

1 голос
/ 14 февраля 2011

Не могли бы вы попробовать

ISNULL(ERP_ServiceProcess.fiStatusNew,0) IN (0, 1, 7, 8)

Не проверено, но может быть быстрее, чем второй запрос.

0 голосов
/ 14 февраля 2011

'ERP_ServiceProcess.fiStatusNew IN (NULL)' оценивается как 'ERP_ServiceProcess.fiStatusNew = NULL', и это всегда ложно.NULL определяется на сервере sql как «неизвестно», а не как «нет значения».Вот почему NULL = NULL или NULL = @var (*) всегда оценивается как ложное.Если у вас есть два неизвестных, вы не можете проверить, равны ли они.Работает только 'is NULL'.

(*) Ну, для сервера sql вы можете отключить ANSI_NULLS , но это не рекомендуется, поскольку это не стандартное поведение sql.

...