Значения SQL и NULL в предложении where - PullRequest
9 голосов
/ 15 октября 2010

Итак, у меня есть простой запрос, который возвращает список продуктов

SELECT     Model, CategoryID
FROM         Products
WHERE     (Model = '010-00749-01') 

Возвращает

010-00749-01    00000000-0000-0000-0000-000000000000
010-00749-01    NULL

Что правильно, поэтому я хотел только продукты, у которых CategoryID не '00000000-0000-0000-0000-000000000000', поэтому у меня есть

SELECT     Model, CategoryID
FROM         Products
WHERE     (Model = '010-00749-01') 
AND (CategoryID <> '00000000-0000-0000-0000-000000000000') 

Но это не возвращает результата. Поэтому я изменил запрос на

SELECT     Model, CategoryID
FROM         Products
WHERE     (Model = '010-00749-01') 
AND ((CategoryID <> '00000000-0000-0000-0000-000000000000') OR  (CategoryID  IS NULL))

Что возвращает ожидаемый результат

010-00749-01    NULL

Может кто-нибудь объяснить мне это поведение? MS SQL Server 2008

Ответы [ 6 ]

9 голосов
/ 15 октября 2010

Ознакомьтесь с полной ссылкой на Books Online - по умолчанию включен ANSI_NULLS, что означает, что вам нужно использовать подход, который вы использовали.В противном случае вы можете переключить эту настройку OFF в начале запроса, чтобы переключить поведение.

Когда SET ANSI_NULLS установлен в ON, оператор SELECT, использующий WHERE column_name = NULL, возвращает ноль строк, даже еслиявляются нулевыми значениями в column_name.Оператор SELECT, который использует WHERE column_name <> NULL, возвращает ноль строк, даже если в column_name есть ненулевые значения....Когда SET ANSI_NULLS включен, все сравнения с нулевым значением оцениваются как UNKNOWN.Когда SET ANSI_NULLS имеет значение OFF, сравнение всех данных с нулевым значением оценивается как TRUE, если значение данных равно NULL.

Вот простой пример, демонстрирующий поведение при сравнении с NULL:

-- This will print TRUE
SET ANSI_NULLS OFF;
IF NULL <> 'A'
    PRINT 'TRUE'
ELSE
    PRINT 'FALSE'

-- This will print FALSE
SET ANSI_NULLS ON;
IF NULL <> 'A'
    PRINT 'TRUE'
ELSE
    PRINT 'FALSE'
2 голосов
/ 15 октября 2010

В общем, вы должны помнить, что NULL обычно означает НЕИЗВЕСТНЫЙ. Это означает, что если вы скажете CategoryID <> '00000000-0000-0000-0000-000000000000', вы должны предположить, что запрос будет возвращать только те значения, которые он ЗНАЕТ, будет соответствовать вашим критериям. Поскольку существует результат NULL (НЕИЗВЕСТНО), он фактически не знает, соответствует ли эта запись вашим критериям и поэтому не будет возвращен в наборе данных.

1 голос
/ 15 октября 2010

В основном, NULL - это отсутствие какого-либо значения . Поэтому попытка сравнить значение NULL в CategoryId со значением varchar в запросе всегда приведет к ложной оценке.

Возможно, вы захотите попробовать использовать функцию COALESCE, например:

SELECT     ModelId, CategoryID 
FROM       Products 
WHERE      (ModelId = '010-00749-01')  
AND        ( COALESCE( CategoryID, '' ) <> '00000000-0000-0000-0000-000000000000' ) 

EDIT

Как отмечено AdaTheDev, функция COALESCE отменяет любые индексы, которые могут существовать в столбце CategoryID, что может повлиять на план запроса и производительность.

1 голос
/ 15 октября 2010

посмотрите на это:

1=1        --true
1=0        --false
null=null  --false
null=1     --false

1<>1       --false
1<>0       --true
null<>null --false
null<>1    --false    <<<--why you don't get the row with: AND (CategoryID <> '00000000-0000-0000-0000-000000000000') 
0 голосов
/ 15 октября 2010

Вы можете попробовать использовать функцию Coalesce для установки значения по умолчанию для полей, которые имеют null:

   SELECT    Model , CategoryID
   FROM      Products
   WHERE     Model = '010-00749-01'
     AND     Coalesce(CategoryID,'') <> '00000000-0000-0000-0000-000000000000'

Я думаю, что проблема заключается в вашем понимании NULL, что в основном означает "ничего такого."Вы ничего не можете сравнить ни с чем, так же, как вы не можете разделить число на 0. Это просто правила математики / науки.

Редактировать: Как указала Ада, это может привести к тому, что индексированное поле больше не будет использовать индекс.

Решение:

  • Вы можете создать индекс, используя функцию объединения: например, create index ... coalesce(field)
  • Вы можете добавить ограничение not null, чтобы запретить использование NULL.
  • Фактическим стандартом для меня является всегда присваивать значения по умолчанию и никогда не разрешать нули
0 голосов
/ 15 октября 2010

Нуль получает специальное лечение.Вы должны явно проверить на ноль.Смотри http://msdn.microsoft.com/en-us/library/ms188795.aspx

...