Clausola CASE T-SQL: come specificare QUANDO NULL


227

Ho scritto una dichiarazione T-SQL simile a questa (quella originale sembra diversa ma voglio dare un semplice esempio qui):

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

Questa istruzione non ha errori di sintassi ma la clausola case sceglie sempre la parte ELSE, anche se last_name è null. Ma perché?

Quello che voglio fare è unire first_name e last_name, ma se last_name è null l'intero nome diventa null:

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

Sai dov'è il problema?

Risposte:


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

4
Il suggerimento COALESCE di Luther è migliore della mia risposta. È leggermente meno efficiente, ma molto più elegante.
Marcelo Cantos,

Gestirlo con qualcosa del genere può essere utile o qualcosa del genere: COALESCE (last_name, '') L'istruzione case, sebbene concisa, è meno gestibile di COALESCE nelle query di grandi dimensioni. Alla fine, hai lo stesso risultato. Se hai bisogno di ottimizzare, controlla i piani di esecuzione ma non ho notato molta differenza.
Anthony Mason,

1
COALESCEfondamentalmente si traduce internamente in CASE. Il problema con CASE è che last_nameviene valutato due volte e può portare ad altri effetti collaterali - vedi questo eccellente articolo . Per risolvere ciò che è stato chiesto, preferirei andare ISNULL(' '+ last_name, '')menzionato nel commento qui sotto.
miroxlav,

41

La parte QUANDO viene confrontata con ==, ma non è possibile confrontare realmente con NULL. Provare

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

invece o COALESCE:

COALESCE(' '+last_name,'')

('' + last_name è NULL quando last_name è NULL, quindi dovrebbe restituire '' in quel caso)


Va bene grazie per le informazioni con == nella parte QUANDO. Non lo sapevo. Con COALESCE funziona anche bene. Non ho pensato che ci siano così tante possibilità per farlo.
meni,

15

Esistono molte soluzioni, ma nessuna copre il motivo per cui la dichiarazione originale non funziona.

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

Dopo il momento, c'è un controllo per l'uguaglianza, che dovrebbe essere vero o falso.

Se una o entrambe le parti di un confronto sono nulle, il risultato del confronto sarà SCONOSCIUTO, che viene trattato come falso nella struttura di un caso. Vedi: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Per evitare ciò, Coalesce è il modo migliore.


11

Data la tua domanda puoi anche fare questo:

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

2
Ciò aggiunge uno spazio ridondante quando last_name è null, che è ciò che l'OP ha cercato di evitare.
Marcelo Cantos,

6
Migliore utilizzo ISNULL(' '+ last_name, '')per prevenire lo spazio ridondante.
Prutswonder,

Sì, l'ho capito dopo che ho pubblicato. Ho pensato di lasciarlo dal momento che ha risolto la parte con cui stava avendo problemi.
Ian Jacobs,

7

Il problema è che null non è considerato uguale a se stesso, quindi la clausola non corrisponde mai.

È necessario verificare esplicitamente null per:

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

5

provare:

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

Questo aggiunge lo spazio al cognome, se è nullo, l'intero spazio + cognome diventa NULL e si ottiene solo un nome, altrimenti si ottiene un primo + spazio + cognome.

questo funzionerà finché l'impostazione predefinita per la concatenazione con stringhe null è impostata:

SET CONCAT_NULL_YIELDS_NULL ON 

questo non dovrebbe essere un problema poiché la OFFmodalità non sarà più disponibile nelle versioni future di SQl Server


4

Il problema è che NULL non è considerato uguale a niente, nemmeno a se stesso, ma la strana parte è che non è uguale a se stesso.

Considerare le seguenti dichiarazioni (che è BTW illegale in SQL Server T-SQL ma è valido in My-SQL, tuttavia questo è ciò che ANSI definisce per null e può essere verificato anche in SQL Server utilizzando le istruzioni case ecc.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Quindi non esiste una risposta vera / falsa alla domanda, ma anche la risposta è nulla.

Ciò ha molte implicazioni, ad esempio in

  1. Istruzioni CASE, in cui qualsiasi valore null utilizzerà sempre la clausola ELSE a meno che non si utilizzi esplicitamente la condizione WHEN IS NULL ( NON la WHEN NULLcondizione )
  2. Concatenazione di stringhe, come
    SELECT a + NULL -- Results in NULL
  3. In una clausola WHERE IN o WHERE NOT IN, come se si desiderassero risultati corretti assicurarsi che nella sottoquery correlata filtrare eventuali valori null.

È possibile ignorare questo comportamento in SQL Server specificando SET ANSI_NULLS OFF, tuttavia questo NON è raccomandato e non dovrebbe essere fatto in quanto può causare molti problemi, semplicemente perché deviazione dello standard.

(Come nota a margine, in My-SQL c'è un'opzione per usare un operatore speciale <=>per il confronto null.)

In confronto, in generale i linguaggi di programmazione null è trattato è un valore regolare ed è uguale a se stesso, tuttavia è il valore NAN che non è uguale a se stesso, ma almeno restituisce "falso" quando lo confronta con se stesso (e quando si verifica che non sia uguale a linguaggi di programmazione diversi hanno implementazioni diverse).

Si noti tuttavia che nei linguaggi di base (ad es. VB ecc.) Non esiste una parola chiave "null" e si utilizza invece la parola chiave "Nothing", che non può essere utilizzata nel confronto diretto e invece è necessario utilizzare "IS" come in SQL, tuttavia è in effetti uguale a se stesso (quando si usano confronti indiretti).


1
"La parte strana è che anche non è uguale a se stesso." => questo non è strano considerando che null in SQL ha il significato 'sconosciuto'. Qualcosa di sconosciuto, molto probabilmente non è lo stesso di qualcos'altro che non è noto.
JDC,

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

1

Quando ti senti frustrato provando questo:

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

Prova questo invece:

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

LEN(ISNULL(last_Name,''))misura il numero di caratteri in quella colonna, che sarà zero se è vuoto o NULL, quindi WHEN 0 THENvaluterà su true e restituirà '' come previsto.

Spero che questa sia un'alternativa utile.

Ho incluso questo caso di prova per SQL Server 2008 e versioni successive:

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
Sei sicuro che funzioni? La maggior parte delle funzioni restituisce NULL quando viene fornito un input NULL e i miei test confermano che ciò vale anche per LEN (), cioè LEN (NULL) restituisce NULL, non 0.
Jason Musgrove,

Pokechu22 è corretto, e questo è lo script corretto: CASE LEN (ISNULL (last_Name, '')) QUANDO 0 POI '' ELSE '' + last_name END AS newlastName
Chef Slagle

0

Jason ha rilevato un errore, quindi funziona ...

Qualcuno può confermare le altre versioni della piattaforma?
Server 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

Oracolo:

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

0

È possibile utilizzare la funzione IsNull

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

se una colonna è nulla otterresti un carattere vuoto

Compatibile con Microsoft SQL Server 2008+


0

Utilizzare la funzione CONCAT disponibile da SQL Server 2012 in poi.

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

0

Ho provato a lanciare una stringa e testare una stringa di lunghezza zero e ha funzionato.

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

0

NULL non equivale a nulla. L'istruzione case in pratica dice quando il valore = NULL .. non colpirà mai.
Esistono anche diverse stored procedure di sistema scritte in modo errato con la sintassi. Vedi sp_addpullsubscription_agent e sp_who2.
Vorrei sapere come avvisare Microsoft di quegli errori in quanto non sono in grado di modificare i processi memorizzati nel sistema.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.