Clause T-SQL CASE: comment spécifier QUAND NULL


228

J'ai écrit une déclaration T-SQL similaire à celle-ci (l'original a l'air différent mais je veux donner un exemple simple ici):

SELECT first_name + 
    CASE last_name WHEN null THEN 'Max' ELSE 'Peter' END AS Name
FROM dbo.person

Cette instruction ne contient aucune erreur de syntaxe, mais la clause case choisit toujours la partie ELSE - également si le nom de famille est nul. Mais pourquoi?

Ce que je veux faire, c'est unir le prénom et le nom de famille, mais si le nom de famille est nul, le nom entier devient nul:

SELECT first_name +
   CASE last_name WHEN null THEN '' ELSE ' ' + last_name END AS Name 
FROM dbo.person

Savez-vous où est le problème?

Réponses:


370
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

4
La suggestion COALESCE de Luther est meilleure que ma réponse. Il est légèrement moins efficace, mais beaucoup plus élégant.
Marcelo Cantos

Le manipuler avec quelque chose comme ça peut être utile, ou quelque chose de similaire: COALESCE (nom_famille, '') L'instruction case, bien que concise, est à mon humble avis moins gérable que COALESCE dans les grandes requêtes. Au final, vous avez le même résultat. Si vous avez besoin d'optimiser, vérifiez les plans d'exécution mais je n'ai pas remarqué beaucoup de différence.
Anthony Mason

1
COALESCEse traduit essentiellement en interne CASE. Le problème avec CASE est qu'il last_nameest évalué deux fois et qu'il peut entraîner d'autres effets secondaires - voir cet excellent article . Pour résoudre ce qui a été demandé, je préfère aller avec ISNULL(' '+ last_name, '')mentionné dans le commentaire ci-dessous.
miroxlav

41

La partie WHEN est comparée à ==, mais vous ne pouvez pas vraiment comparer avec NULL. Essayer

CASE WHEN last_name is NULL  THEN ... ELSE .. END

à la place ou COALESCE:

COALESCE(' '+last_name,'')

('' + last_name est NULL lorsque last_name est NULL, donc il devrait retourner '' dans ce cas)


D'accord merci pour les informations avec le == dans la partie QUAND. Je ne le savais pas. Avec COALESCE, cela fonctionne aussi très bien. Je n'ai pas pensé qu'il y avait autant de possibilités pour le faire.
meni

15

Il existe de nombreuses solutions, mais aucune ne explique pourquoi la déclaration d'origine ne fonctionne pas.

CASE last_name WHEN null THEN '' ELSE ' '+last_name

Après le moment, il y a une vérification de l'égalité, qui doit être vraie ou fausse.

Si une ou les deux parties d'une comparaison sont nulles, le résultat de la comparaison sera INCONNU, qui est traité comme faux dans une structure de cas. Voir: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Pour éviter cela, Coalesce est le meilleur moyen.


11

Compte tenu de votre requête, vous pouvez également le faire:

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person

2
Cela ajoute un espace redondant lorsque last_name est null, ce que l'OP tentait d'éviter.
Marcelo Cantos

6
Mieux utiliser ISNULL(' '+ last_name, '')pour éviter l'espace redondant.
Prutswonder

Ouais, je l'ai réalisé après avoir posté. Je pensais que je le laisserais car cela résolu la partie avec laquelle il avait des problèmes.
Ian Jacobs

7

Le problème est que null n'est pas considéré comme égal à lui-même, donc la clause ne correspond jamais.

Vous devez vérifier explicitement null:

SELECT CASE WHEN last_name is NULL THEN first_name ELSE first_name + ' ' + last_name

5

essayer:

SELECT first_name + ISNULL(' '+last_name, '') AS Name FROM dbo.person

Cela ajoute l'espace au nom de famille, s'il est nul, l'espace + le nom entier va à NULL et vous obtenez seulement un prénom, sinon vous obtenez un premier + espace + nom.

cela fonctionnera tant que le paramètre par défaut pour la concaténation avec des chaînes nulles est défini:

SET CONCAT_NULL_YIELDS_NULL ON 

cela ne devrait pas être un problème car le OFFmode disparaîtra dans les futures versions de SQl Server


4

Le problème est que NULL n'est pas considéré comme égal à rien, même pas à lui-même, mais la partie étrange est qu'il n'est pas non plus égal à lui-même.

Considérez les instructions suivantes (ce qui est illégal en BTW dans SQL Server T-SQL mais valide dans My-SQL, mais c'est ce que ANSI définit pour null, et peut être vérifié même dans SQL Server en utilisant des instructions case, etc.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Il n'y a donc pas de réponse vraie / fausse à la question, mais la réponse est également nulle.

Cela a de nombreuses implications, par exemple dans

  1. Instructions CASE, dans lesquelles toute valeur nulle utilisera toujours la clause ELSE, sauf si vous utilisez explicitement la condition WHEN IS NULL ( PAS la WHEN NULLcondition )
  2. Concaténation de chaînes, as
    SELECT a + NULL -- Results in NULL
  3. Dans une clause WHERE IN ou WHERE NOT IN, comme si vous vouliez des résultats corrects, assurez-vous dans la sous-requête corrélée de filtrer toutes les valeurs nulles.

On peut remplacer ce comportement dans SQL Server en spécifiant SET ANSI_NULLS OFF, mais ce n'est PAS recommandé et ne doit pas être fait car cela peut provoquer de nombreux problèmes, simplement en raison de la déviation de la norme.

(En remarque, dans My-SQL, il existe une option pour utiliser un opérateur spécial <=>pour la comparaison nulle.)

En comparaison, dans les langages de programmation généraux, null est traité est une valeur régulière et est égal à lui-même, cependant c'est la valeur NAN qui n'est pas non plus égale à elle-même, mais au moins elle retourne 'false' lors de la comparaison avec elle-même, (et lors de la vérification de non égal à différents langages de programmation ont différentes implémentations).

Notez cependant que dans les langages de base (c'est-à-dire VB, etc.), il n'y a pas de mot-clé "null" et à la place, on utilise le mot-clé "Nothing", qui ne peut pas être utilisé en comparaison directe et à la place, il faut utiliser "IS" comme dans SQL, cependant, il est en fait égal à lui-même (lors de l'utilisation de comparaisons indirectes).


1
"La partie étrange est que ce n'est pas non plus égal à lui-même." => ce n'est pas étrange étant donné que null en SQL a le sens 'inconnu'. Quelque chose d'inconnu n'est probablement pas le même que quelque chose qui n'est pas connu.
JDC

1
CASE  
    WHEN last_name IS null THEN '' 
    ELSE ' ' + last_name 
END

1

Lorsque vous êtes frustré d'essayer ceci:

CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

Essayez plutôt celui-ci:

CASE LEN(ISNULL(last_Name,''))
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

LEN(ISNULL(last_Name,''))mesure le nombre de caractères dans cette colonne, qui sera zéro, qu'elle soit vide ou NULL, WHEN 0 THENsera donc évalué à vrai et retournera le '' comme prévu.

J'espère que c'est une alternative utile.

J'ai inclus ce cas de test pour SQL Server 2008 et supérieur:

DECLARE @last_Name varchar(50) = NULL

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

SET @last_Name = 'LastName'

SELECT 
CASE LEN(ISNULL(@last_Name,''))
WHEN 0 THEN '' 
ELSE 'A ' + @last_name
END AS newlastName

2
Êtes-vous sûr que cela fonctionne? La plupart des fonctions renvoient NULL lorsqu'elles reçoivent une entrée NULL et mes tests confirment que c'est également vrai pour LEN () c'est-à-dire que LEN (NULL) renvoie NULL, pas 0.
Jason Musgrove

Pokechu22 est correct, et voici le script corrigé: CASE LEN (ISNULL (last_Name, '')) WHEN 0 THEN '' ELSE '' + last_name END AS newlastName
Chef Slagle

0

Jason a détecté une erreur, donc cela fonctionne ...

Quelqu'un peut-il confirmer les autres versions de la plate-forme?
Serveur SQL:

SELECT
CASE LEN(ISNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

MySQL:

SELECT
CASE LENGTH(IFNULL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

Oracle:

SELECT
CASE LENGTH(NVL(last_name,'')) 
WHEN 0 THEN '' 
ELSE ' ' + last_name
END AS newlastName

0

Vous pouvez utiliser la fonction IsNull

select 
    isnull(rtrim(ltrim([FirstName]))+' ','') +
    isnull(rtrim(ltrim([SecondName]))+' ','') +
    isnull(rtrim(ltrim([Surname]))+' ','') +
    isnull(rtrim(ltrim([SecondSurname])),'')
from TableDat

si une colonne est nulle, vous obtiendrez un caractère vide

Compatible avec Microsoft SQL Server 2008+


0

Utilisez la fonction CONCAT disponible dans SQL Server 2012.

SELECT CONCAT([FirstName], ' , ' , [LastName]) FROM YOURTABLE

0

J'ai essayé de lancer une chaîne et de tester une chaîne de longueur nulle et cela a fonctionné.

CASE 
   WHEN LEN(CAST(field_value AS VARCHAR(MAX))) = 0 THEN 
       DO THIS
END AS field 

0

NULL ne vaut rien. L'instruction case dit essentiellement que lorsque la valeur = NULL .. elle ne frappera jamais.
Il existe également plusieurs procédures stockées système qui ne sont pas écrites correctement avec votre syntaxe. Voir sp_addpullsubscription_agent et sp_who2.
J'aimerais savoir comment informer Microsoft de ces erreurs car je ne suis pas en mesure de modifier les procs stockés du système.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.