Opérateur <>! = Différent de NULL

271

Quelqu'un pourrait-il expliquer le comportement suivant en SQL?

SELECT * FROM MyTable WHERE MyColumn != NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn <> NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn IS NOT NULL (568 Results)
Maxim Gershkovich
la source

Réponses:

309

<>est le standard SQL-92; !=est son équivalent. Les deux évaluent les valeurs, ce qui NULLn'est pas - NULLest un espace réservé pour dire qu'il n'y a pas de valeur.

C'est pourquoi vous ne pouvez utiliser IS NULL/ IS NOT NULLcomme prédicats que pour de telles situations.

Ce comportement n'est pas spécifique à SQL Server. Tous les dialectes SQL conformes aux normes fonctionnent de la même manière.

Remarque : Pour comparer si votre valeur n'est pas nulle , vous utilisez IS NOT NULL, tandis que pour comparer avec une valeur non nulle , vous utilisez <> 'YOUR_VALUE'. Je ne peux pas dire si ma valeur est égale ou non à NULL, mais je peux dire si ma valeur est NULL ou NOT NULL. Je peux comparer si ma valeur est autre que NULL.

Poneys OMG
la source
4
En fait, je crois <>que c'est dans la spécification 92, mais la plupart des fournisseurs prennent en charge !=et / ou il est inclus dans une spécification ultérieure comme 99 ou 03.
Thomas
2
@ Thomas: Oracle n'a pas pris en charge !=avant ~ 9i si je comprends bien, ce qui a apporté beaucoup de syntaxe ANSI-92. Ma conviction est que MySQL est similaire, démarrant le support en 4.x.
OMG Ponies
Cela semblerait suggérer que cela !=aurait pu être inclus dans une spécification ultérieure comme alternative à <>. Je n'ai pas mes mains sur de nouvelles spécifications, donc je ne peux pas en être sûr.
Thomas
2
Le résultat est-il WHERE MyColumn != NULLou WHERE MyColumn = NULLdéterministe? Ou en d'autres termes, est-il garanti de toujours retourner 0 ligne, peu importe si elle MyColumnpeut être annulée ou non dans la base de données?
Slauma
11
Il convient également de noter que, car !=n'évalue que les valeurs, faire quelque chose comme WHERE MyColumn != 'somevalue'ne retournera pas les enregistrements NULL.
jsumrall
88

NULL n'a pas de valeur et ne peut donc pas être comparé à l'aide des opérateurs de valeur scalaire.

En d'autres termes, aucune valeur ne peut jamais être égale (ou non égale) à NULL car NULL n'a pas de valeur.

Par conséquent, SQL a des prédicats IS NULL et IS NOT NULL spéciaux pour traiter NULL.

Barry Brown
la source
3
+1. Et, contrairement à l'instruction OP, ce n'est pas "Microsoft SQL". La logique trinaire est définie dans la norme SQL et MS en ce point adhère à la norme.
TomTom
6
Je ne suggérais pas qu'il s'agit d'un comportement réservé à Microsoft. Je disais simplement que je l'ai observé sur Microsoft SQL Server.
Maxim Gershkovich
13
Par intérêt, y a-t-il des situations où ce comportement (attendu) est utile? Il me semble juste de ne 'a' != nullPAS avoir retourné de valeur ( true/ 1) est contre-intuitif et me rattrape de temps en temps! J'aurais pensé "une certaine valeur par rapport à aucune valeur" serait toujours "pas égal", mais peut-être que c'est juste moi?!?
DarthPablo
1
Je pense qu'il est intéressant que les gens décrivent NULL comme « n'ayant aucune valeur ». Similaire, alors, à dire que le nombre 1 «a une valeur» alors qu'il est en fait une valeur. Mais NULL représente la non-valeur ..
systemaddict
Comme solution de contournement manuelle, vous pouvez généralement SELECT * FROM MyTable WHERE coalesce(MyColumn, 'x') <> 'x'attribuer une constante s'il s'agit d'une valeur NULL, à condition de donner un type de données approprié pour la valeur sentinelle x (dans ce cas, une chaîne / caractère). Il s'agit de la syntaxe TSQL mais Oracle et d'autres moteurs ont des fonctionnalités similaires.
systemaddict
26

Notez que ce comportement est le comportement par défaut (ANSI).

Si vous:

 SET ANSI_NULLS OFF

http://msdn.microsoft.com/en-us/library/ms188048.aspx

Vous obtiendrez des résultats différents.

SET ANSI_NULLS OFF va apparemment disparaître à l'avenir ...

Cade Roux
la source
8
+1 ... pas assez tôt. Maintenant, quand puis-je obtenir des NULL "en double" dans un index? :(
Vous pouvez obtenir des NULL en double dans un index SQL Server en ajoutant une clause WHERE dans un index filtré (par exemple create unique index UK_MyTable on MyTable (Column) where Column is not null): msdn.microsoft.com/en-us/library/cc280372.aspx
Anthony Mills
3
Note de la documentation: Lorsque SET ANSI_NULLSest désactivé, les opérateurs de comparaison Égaux (=) et Non égaux à (<>) ne suivent pas la norme ISO. Une instruction SELECT qui utilise WHERE column_name = NULLrenvoie les lignes qui ont des valeurs nulles dans nom_colonne. Une instruction SELECT qui utilise WHERE column_name <> NULLrenvoie les lignes qui ont des valeurs non nulles dans la colonne. En outre, une instruction SELECT qui utilise WHERE column_name <> XYZ_valuerenvoie toutes les lignes qui ne sont pas XYZ_value et qui ne sont pas NULL. À mon humble avis, cette dernière déclaration semble un peu étrange dans son exclusion des null des résultats!
DarthPablo
4
Remarque importante du document msdn : dans une future version de SQL Server [plus récente que 2014], ANSI_NULLS sera toujours activé et toutes les applications qui définissent explicitement l'option sur OFF généreront une erreur. Évitez d'utiliser cette fonctionnalité dans de nouveaux travaux de développement et prévoyez de modifier les applications qui utilisent actuellement cette fonctionnalité.
Otiel
7

En SQL, tout ce que vous évaluez / calculez avec des NULLrésultats dans INCONNU

C'est pourquoi SELECT * FROM MyTable WHERE MyColumn != NULLou SELECT * FROM MyTable WHERE MyColumn <> NULLvous donne 0 résultats.

Pour vérifier les NULLvaleurs, la fonction isNull est fournie.

De plus, vous pouvez utiliser l' ISopérateur comme vous l'avez utilisé dans la troisième requête.

J'espère que cela t'aides.

Mahendra Liya
la source
"En SQL, tout ce que vous évaluez / calculez avec des résultats NULL en 'NULL'" - incorrect. Le résultat que vous voulez dire est INCONNU.
onedaywhen
@MahendraLiya, la fonction isNull n'est pas fournie pour vérifier les valeurs NULL, mais elle " remplace NULL par la valeur de remplacement spécifiée ". Vous devez utiliser IS NULL ou IS NOT NULL au lieu de ISNULL qui est une chose différente.
Ingénieur inversé
6

Nous utilisons

SELECT * FROM MyTable WHERE ISNULL(MyColumn, ' ') = ' ';

pour renvoyer toutes les lignes où MyColumn est NULL ou toutes les lignes où MyColumn est une chaîne vide. Pour de nombreux "utilisateurs finaux", le problème NULL vs chaîne vide est une distinction sans besoin ni point de confusion.

Jeff Mergler
la source
5

Je ne vois tout simplement pas la raison fonctionnelle et transparente pour que les valeurs nulles ne soient pas comparables à d'autres valeurs ou à d'autres valeurs nulles, car nous pouvons clairement les comparer et dire qu'elles sont identiques ou non dans notre contexte. C'est marrant. Juste à cause de certaines conclusions logiques et de cohérence, nous devons constamment nous en préoccuper. Il n'est pas fonctionnel, le rend plus fonctionnel et laisse aux philosophes et aux scientifiques le soin de conclure s'il est cohérent ou non et détient-il une «logique universelle». :) Quelqu'un peut dire que c'est à cause des index ou de quelque chose d'autre, je doute que ces choses ne puissent pas être faites pour prendre en charge les valeurs nulles comme les valeurs. C'est la même chose que de comparer deux verres vides, l'un est un verre de vigne et l'autre est un verre de bière, nous ne comparons pas les types d'objets mais les valeurs qu'ils contiennent, de la même manière que vous pourriez comparer int et varchar, avec null it ' s encore plus facile, ce n'est rien et ce que deux néant ont en commun, ils sont les mêmes, clairement comparables par moi et par tous ceux qui écrivent sql, car nous brisons constamment cette logique en les comparant de manière étrange en raison de certaines normes ANSI. Pourquoi ne pas utiliser la puissance informatique pour le faire pour nous et je doute que cela ralentisse les choses si tout ce qui est lié est construit dans cet esprit. "Ce n'est pas nul c'est rien", ce n'est pas Apple c'est apfel, allez ... Fonctionnellement c'est ton ami et il y a aussi de la logique ici. En fin de compte, la seule chose qui compte est la fonctionnalité et l'utilisation de null de cette manière apporte plus ou moins de fonctionnalités et de facilité d'utilisation. Est-ce plus utile? parce que nous brisons constamment cette logique en les comparant de manière étrange à cause de certaines normes ANSI. Pourquoi ne pas utiliser la puissance de l'ordinateur pour le faire pour nous et je doute que cela ralentisse les choses si tout ce qui est lié est construit dans cet esprit. "Ce n'est pas nul c'est rien", ce n'est pas Apple c'est apfel, allez ... Fonctionnellement c'est ton ami et il y a aussi de la logique ici. En fin de compte, la seule chose qui compte est la fonctionnalité et l'utilisation de null de cette manière apporte plus ou moins de fonctionnalités et de facilité d'utilisation. Est-ce plus utile? parce que nous brisons constamment cette logique en les comparant de manière étrange à cause de certaines normes ANSI. Pourquoi ne pas utiliser la puissance de l'ordinateur pour le faire pour nous et je doute que cela ralentisse les choses si tout ce qui est lié est construit dans cet esprit. "Ce n'est pas nul c'est rien", ce n'est pas Apple c'est apfel, allez ... Fonctionnellement c'est ton ami et il y a aussi de la logique ici. En fin de compte, la seule chose qui compte est la fonctionnalité et l'utilisation de null de cette manière apporte plus ou moins de fonctionnalités et de facilité d'utilisation. Est-ce plus utile? s pas apple c'est apfel, allez ... Fonctionnellement est ton ami et il y a aussi de la logique ici. En fin de compte, la seule chose qui compte est la fonctionnalité et l'utilisation de null de cette manière apporte plus ou moins de fonctionnalités et de facilité d'utilisation. Est-ce plus utile? s pas apple c'est apfel, allez ... Fonctionnellement est ton ami et il y a aussi de la logique ici. En fin de compte, la seule chose qui compte est la fonctionnalité et l'utilisation de null de cette manière apporte plus ou moins de fonctionnalités et de facilité d'utilisation. Est-ce plus utile?

Considérez ce code:

SELECT CASE WHEN NOT (1 = null or (1 is null and null is null)) THEN 1 ELSE 0 end

Combien d'entre vous savent ce que ce code renverra? Avec ou sans NOT, il renvoie 0. Pour moi, ce n'est pas fonctionnel et c'est déroutant. En c #, tout est comme il se doit, les opérations de comparaison renvoient de la valeur, logiquement cela produit aussi de la valeur, car si ce n'est pas le cas, il n'y a rien à comparer (sauf. Rien :)). Ils ont juste "dit": n'importe quoi par rapport à null "renvoie" 0 et cela crée de nombreuses solutions de contournement et des maux de tête.

C'est le code qui m'a amené ici:

where a != b OR (a is null and b IS not null) OR (a IS not null and b IS null)

J'ai juste besoin de comparer si deux champs (où) ont des valeurs différentes, je pourrais utiliser la fonction, mais ...

Hrvoje Batrnek
la source
4

NULL Ne peut être comparé à aucune valeur à l'aide des opérateurs de comparaison. NULL = NULL est faux. Null n'est pas une valeur. L'opérateur IS est spécialement conçu pour gérer les comparaisons NULL.

Vincent Ramdhanie
la source
5
J'ai toujours aimé les gens confus quand j'utilise parfois null = nulloù l'on pourrait utiliser 1=0dans une requête ad hoc. Et s'ils se plaignent, je le change en null != null:)
SWeko
8
"NULL = NULL est faux" Ce n'est pas le cas. NULL = NULL est évalué à inconnu et non faux.
nvogel
@dportas c'est vrai mais je voulais dire que dans un conditionnel, il ne sera pas évalué comme vrai.
Vincent Ramdhanie
@VincentRamdhanie ni faux; en fait, en postgres, il sera évalué comme NULL
Pere
2

Vieille question, mais ce qui suit pourrait offrir plus de détails.

nullne représente aucune valeur ou une valeur inconnue. Il ne précise pas pourquoi il n'y a pas de valeur, ce qui peut conduire à une certaine ambiguïté.

Supposons que vous exécutez une requête comme celle-ci:

SELECT *
FROM orders
WHERE delivered=ordered;

c'est-à-dire que vous recherchez des lignes où le orderedetdelivered dates sont identiques.

Que faut-il attendre lorsqu'une ou les deux colonnes sont nulles?

Parce qu'au moins une des dates est inconnue, vous ne pouvez pas vous attendre à dire que les 2 dates sont les mêmes. C'est également le cas lorsque les deux dates sont inconnues: comment peuvent-elles être les mêmes si nous ne savons même pas ce qu'elles sont?

Pour cette raison, toute expression considérée nullcomme une valeur doit échouer. Dans ce cas, il ne correspondra pas. C'est également le cas si vous essayez ce qui suit:

SELECT *
FROM orders
WHERE delivered<>ordered;

Encore une fois, comment pouvons-nous dire que deux valeurs ne sont pas identiques si nous ne savons pas ce qu'elles sont.

SQL a un test spécifique pour les valeurs manquantes:

IS NULL

Plus précisément, il ne s'agit pas de comparer des valeurs, mais plutôt de rechercher des valeurs manquantes .

Enfin, en ce qui concerne l' !=opérateur, pour autant que je sache, il ne figure en fait dans aucune des normes, mais il est très largement soutenu. Il a été ajouté pour que les programmeurs de certaines langues se sentent plus à l'aise. Franchement, si un programmeur a du mal à se souvenir de la langue qu'il utilise, il commence mal.

Manngo
la source
C'est la même "logique" "absurde" que @Hove décrit dans sa réponse. La vérité est que, dans ce contexte, il n'est pas nécessaire d'avoir cet attirail supplémentaire; on pourrait facilement supposer que lorsque nous comparons quelque chose à un, NULLnous voulons dire que nous comparons une valeur à «avoir unNULL valeur», et non pas la valeur à «la valeur indéterminée que la sous NULL- couche « a », mais que nous ne savons pas ", ce que nous ne pourrons évidemment jamais savoir. Cela faciliterait vraiment les choses.
Pere
@Pere Je ne dirais pas que c'est strictement «absurde», et je ne suis pas sûr que l'écriture IS NULLsoit beaucoup plus ardue que l'écriture = NULL. Je pense qu'il serait plus cohérent s'il WHERE columnA = columnBa la même interprétation que WHERE columnA = NULL, plutôt que de traiter ce dernier comme un cas spécial. N'oubliez pas que ce NULLn'est pas une valeur. Dans les langages de programmation où il est légitime de le tester, variable == nullc'est parce qu'il nulla une signification différente; il ne représente pas quelque chose d'inconnu, mais une réinitialisation délibérée d'une valeur. Ce n'est pas le cas avec SQL.
Manngo
C'est pourquoi je l'ai mis entre guillemets, @Mangoo;) (et aussi "logique"). Ne vous fâchez pas contre moi; Je parlais du "raisonnement" de l'ANSI, pas de votre explication. Je suis d'accord en ce qu'il n'y a pas de frais généraux entre IS NULLET =NULLdans votre dernier exemple. Mais jetez un œil au dernier de Hover. Je suis fatigué de le vivre encore et encore, d'avoir à faire des tonnes de choses inutiles? vérification supplémentaire ...
Pere
1

Je voudrais suggérer ce code que j'ai fait pour trouver s'il y a un changement dans une valeur, iétant la nouvelle valeur et détant l'ancienne (bien que l'ordre n'ait pas d'importance). D'ailleurs, un changement de valeur à null ou vice versa est un changement mais de null à null ne l'est pas (bien sûr, de la valeur à une autre valeur est un changement mais de la valeur à la même chose ne l'est pas).

CREATE FUNCTION [dbo].[ufn_equal_with_nulls]
(
    @i sql_variant,
    @d sql_variant
)
RETURNS bit
AS
BEGIN
    DECLARE @in bit = 0, @dn bit = 0
    if @i is null set @in = 1
    if @d is null set @dn = 1

    if @in <> @dn
        return 0

    if @in = 1 and @dn = 1
        return 1

    if @in = 0 and @dn = 0 and @i = @d
        return 1

    return 0

END

Pour utiliser cette fonction, vous pouvez

declare @tmp table (a int, b int)
insert into @tmp values
(1,1),
(1,2),
(1,null),
(null,1),
(null,null)

---- in select ----
select *, [dbo].[ufn_equal_with_nulls](a,b) as [=] from @tmp

---- where equal ----
select *,'equal' as [Predicate] from @tmp where  [dbo].[ufn_equal_with_nulls](a,b) = 1

---- where not equal ----
select *,'not equal' as [Predicate] from @tmp where  [dbo].[ufn_equal_with_nulls](a,b) = 0

Les résultats sont:

---- in select ----
a   b   =
1   1   1
1   2   0
1   NULL    0
NULL    1   0
NULL    NULL    1

---- where equal ----
1   1   equal
NULL    NULL    equal

---- where not equal ----
1   2   not equal
1   NULL    not equal
NULL    1   not equal

L'utilisation de sql_variant le rend compatible pour une variété de types

Yariv de Botton
la source