41 votes

Quand est-ce "PAS" pas une négation?

Pourquoi faire les deux à la suite du retour à zéro? Sûrement la deuxième est une négation de la première? Je suis à l'aide de 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

50voto

Luaan Points 8934

C'est une négation. Cependant, vous devez comprendre ANSI Null - une négation d'une valeur NULL est également une valeur NULL. Et la valeur NULL est une falsy valeur de vérité.

Par conséquent, si l'un de vos arguments est null, le résultat de l' @a = @b sera nulle (falsy), et une négation de la qui sera aussi un null (falsy).

Pour utiliser la négation de la manière que vous voulez, vous avez besoin de se débarrasser de la valeur NULL. Toutefois, il pourrait être plus facile de simplement inverser les résultats de la comparaison à la place:

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

Qui vous permettra de toujours vous donner soit 1, 0 ou 0, 1.

EDIT:

Comme jpmc26 noté, il pourrait être utile de développer un peu plus sur la façon dont les valeurs null de se comporter de sorte que vous n'obtenez pas l'idée qu'un seul NULL fera tout NULL. Il y a des opérateurs qui ne sont pas toujours de retour null lorsque l'un de leurs arguments est null - l'exemple le plus évident étant l' is null, bien sûr.

Dans une plus large exemple, les opérateurs logiques en T-SQL utilisation de Kleene est l'algèbre (ou quelque chose de similaire), qui définit les valeurs de vérité de l' OR expression comme suit:

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

(AND est analogue, comme le sont les autres opérateurs)

Donc vous pouvez voir que si au moins l'un des arguments est vrai, le résultat sera également vrai, même si l'autre est une inconnue ("null"). Ce qui signifie également qu' not(T or U) vous donnera un falsy valeur de vérité, tout en not(F or U) sera également vous donner un falsy valeur de vérité, malgré F or U étant falsy - depuis F or U est U, et not(U) également U, qui est falsy.

C'est important d'expliquer pourquoi votre expression fonctionne de la manière que vous l'attendiez quand les deux arguments sont null - @a is null and @b is null a la valeur true, et true or unknown évalue true.

7voto

sagi Points 32665

Ce "bizarre" comportement que vous rencontrez soit causée par l' NULL valeurs.

La négation de l' NOT (Something that returns NULL) n'est TRUE , c'est encore NULL .

E. G.

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

En plus de ce qui été dit ici, vous pouvez éviter ce problème en utilisant

SET ANSI_NULLS OFF

Qui permettra à l'optimiseur pour traiter l' NULL que la valeur normale, et de revenir TRUE\FALSE . Vous devriez noter que ce n'est pas du tout recommandé, et vous devriez l'éviter !

4voto

Kannan Kandasamy Points 9306

C'est le problème avec @a=@b, si cette valeur est nulle, alors il va être un problème

Si vous essayez de code ci-dessous va donner des résultats corrects

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

0voto

Matt Points 3445

NOT est toujours une négation. La raison de ce comportement de T-SQL se trouve dans le fait qu' null des valeurs sont traités de manière spécifique en fonction d'une base de données de configuration (connu sous le nom ansi_nulls). En fonction de ce paramètre, null sont traitées de la même manière que toute autre valeur ou il est traité comme "valeur non définie". Dans ce cas, toutes les expressions contenant des valeurs null sont considérées comme non valides.

En outre, l'expression

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

ne vise que le cas lorsque les deux variables sont NULL, il ne traite pas de cas lorsque @a ou @b est NULL. Si cela se produit, le résultat dépend de la valeur de ansi_nulls: si c'est on, alors le résultat de l' @a = @b toujours false si l'une des variables est - NULL.

Si ansi_nulls est off, alors NULL est traitée comme une valeur et se comporte comme prévu.

Pour éviter de tels comportements inattendus, vous devez couvrir tous les cas, comme suit:

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 (@a=@b) THEN 1
    ELSE 0
    END 

Notez que dans cet exemple, tous les null cas sont traités avant que l' @a=@b de cas est vérifié (en CASE - déclaration, l' WHENs'sont traitées dans l'ordre où elles apparaissent, et si une condition est satisfaite, le traitement est terminé et que la valeur spécifiée est retourné).


Pour tester tous les possibles (pertinent) des combinaisons, vous pouvez utiliser ce 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 

Il retourne:

result of query

En d'autres termes, dans ce script, null est traitée comme une valeur, par conséquent a='a' et b=null retours 0, ce qui est ce que vous avez prévu. Seulement si les deux variables sont égales (ou les deux null), elle renvoie 1.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X