Подтвердить что ты не робот

Когда "НЕ" не отрицание?

Почему оба возвращаемых значения равны нулю? Неужели второе - это отрицание первого? Я использую SQL Server 2008.

DECLARE 
    @a VARCHAR(10) = NULL ,
    @b VARCHAR(10) = 'a'

SELECT  
    CASE WHEN ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END , -- Returns 0
    CASE WHEN NOT ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END -- Also returns 0
4b9b3361

Ответ 1

Это отрицание. Однако вам нужно понять ANSI NULLs - отрицание NULL также является NULL. И NULL - значение ложной истины.

Следовательно, если любой из ваших аргументов равен NULL, результат @a = @b будет null (фальшивый), а отрицание этого также будет нулевым (фальшивым).

Чтобы использовать отрицание так, как вы хотите, вам нужно избавиться от NULL. Однако проще было бы просто изменить результаты сравнения:

case when (...) then 1 else 0 end,
case when (...) then 0 else 1 end

Это всегда даст вам 1, 0 или 0, 1.

EDIT:

Как отмечалось в jpmc26, может быть полезно развернуть бит о том, как ведут себя NULL, так что вы не понимаете, что один NULL сделает все NULL. Существуют операторы, которые не всегда возвращают NULL, когда один из их аргументов равен нулю - наиболее очевидным примером является is null, конечно.

В более широком примере логические операторы в T-SQL используют алгебру Клейна (или что-то подобное), которая определяет значения истинности выражения OR следующим образом:

  | T | U | F
T | T | T | T
U | T | U | U
F | T | U | F

(AND аналогичен, как и другие операторы)

Итак, вы можете видеть, что если хотя бы один из аргументов истинен, результат также будет истинным, даже если другой неизвестен ( "null" ). Это также означает, что not(T or U) даст вам значение ложной истины, а not(F or U) также даст вам значение ложной истины, несмотря на то, что F or U является ложным - поскольку F or U есть U, а not(U) также U, что является ложным.

Это важно, чтобы объяснить, почему ваше выражение работает так, как вы ожидаете, когда оба аргумента равны нулю. @a is null and @b is null оценивает значение true, а true or unknown оценивается как true.

Ответ 2

Это странное поведение, с которым вы сталкиваетесь, вызвано значениями NULL.

Отрицание NOT (Something that returns NULL) не TRUE, оно все еще NULL.

например.

SELECT * FROM <Table> WHERE <Column> = null -- 0 rows 
SELECT * FROM <Table> WHERE NOT (<Column> = null) -- Still 0 rows

В дополнение к тому, что было сказано здесь, вы можете избежать этого поведения, используя

SET ANSI_NULLS OFF

Это позволит оптимизатору обрабатывать NULL как нормальное значение и возвращать TRUE\FALSE. Вы должны заметить, что это не рекомендуется вообще, и вам следует избегать этого!

Ответ 3

Это проблема с @a = @b, если любое из этого значения равно null, то это будет проблема

Если вы попробуете ниже код, вы получите правильные результаты

DECLARE 
    @a VARCHAR(10) = NULL ,
    @b VARCHAR(10) = 'a'

SELECT  
    CASE WHEN ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END , -- returns 0
    CASE WHEN NOT ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR ISNULL(@a,-1) = ISNULL(@b,-1)
                  ) THEN 1
         ELSE 0
    END -- also returns 0

Ответ 4

NOT всегда является отрицанием. Причина такого поведения T-SQL заключается в том, что значения null обрабатываются особым образом в зависимости от настройки конфигурации базы данных (известной как ansi_nulls). В зависимости от этой настройки null обрабатывается так же, как любое другое значение, или обрабатывается как "значение не установлено". В этом случае все выражения, содержащие нулевые значения, считаются недействительными.

Кроме того, выражение

(@a IS NULL AND @b IS NULL)
OR 
@a = @b

охватывает только случай, когда обе переменные равны null, это не касается случаев, когда либо @a, либо @b равно null. Если это произойдет, результат зависит от параметра ansi_nulls: если он on, тогда результат @a = @b всегда false, если одна из переменных null.

Если ansi_nulls - off, тогда null рассматривается как значение и ведет себя так, как вы ожидаете.

Чтобы избежать такого неожиданного поведения, вы должны охватить все случаи следующим образом:

DECLARE 
    @a VARCHAR(10) = 'a',
    @b VARCHAR(10) = null

SELECT  
    CASE 
        WHEN (@a IS NOT null AND @b IS null) THEN 0
        WHEN (@a IS null AND @b IS NOT null) THEN 0
        WHEN (@a IS null AND @b IS null) THEN 1
        WHEN (@[email protected]) THEN 1
    ELSE 0
    END 

Примечание, что в этом примере все нулевые случаи рассматриваются до проверки события @[email protected] (в операторе CASE, WHEN обрабатываются в порядке по мере их появления, и если условие соответствует, обработка завершена и возвращается указанное значение).


Чтобы проверить все возможные (соответствующие) комбинации, вы можете использовать этот script:

DECLARE @combinations TABLE (
    a VARCHAR(10),b VARCHAR(10)
    )

INSERT INTO @combinations
    SELECT 'a', null 
    UNION SELECT null, 'b'
    UNION SELECT 'a', 'b'
    UNION SELECT null, null
    UNION SELECT 'a', 'a'

SELECT a, b,
    CASE 
        WHEN (a IS NOT null AND b IS null) THEN 0
        WHEN (a IS null AND b IS NOT null) THEN 0
        WHEN (a IS null AND b IS null) THEN 1
        WHEN (a=b) THEN 1
    ELSE 0
    END as result
from @combinations
order by result 

Он возвращает:

результат запроса

Другими словами, в этом script null рассматривается как значение, поэтому a='a' и b=null возвращает 0, что и ожидалось. Только если обе переменные равны (или оба null), он возвращает 1.