Cláusula CASO T-SQL: Cómo especificar CUANDO ES NULO

227

Escribí una declaración T-SQL similar a esta (la original se ve diferente pero quiero dar un ejemplo fácil aquí):

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

Esta declaración no tiene ningún error de sintaxis, pero la cláusula case siempre elige la parte ELSE, también si el last_name es nulo. ¿Pero por qué?

Lo que quiero hacer es unir first_name y last_name, pero si last_name es nulo, el nombre completo se vuelve nulo:

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

¿Sabes dónde está el problema?

meni
fuente

Respuestas:

369
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END
Marcelo Cantos
fuente
44
La sugerencia de @ Luther COALESCE es mejor que mi respuesta. Es marginalmente menos eficiente, pero mucho más elegante.
Marcelo Cantos
Manejarlo con algo como esto puede ser útil, o algo por el estilo: COALESCE (last_name, '') La declaración del caso, aunque concisa, es menos fácil de mantener que COALESCE en consultas grandes. Al final, tienes el mismo resultado. Si necesita optimizar, verifique los planes de ejecución, pero no he notado mucha diferencia.
Anthony Mason
1
COALESCEbásicamente se traduce internamente en CASE. El problema con CASE es que last_namese evalúa dos veces y puede provocar otros efectos secundarios; consulte este excelente artículo . Para resolver lo que se pidió, preferiría ir con lo ISNULL(' '+ last_name, '')mencionado en el comentario a continuación.
miroxlav
41

La parte WHEN se compara con ==, pero realmente no se puede comparar con NULL. Tratar

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

en cambio o COALESCE:

COALESCE(' '+last_name,'')

('' + last_name es NULL cuando last_name es NULL, por lo que debería volver '' en ese caso)

Mainframe nórdico
fuente
Bien, gracias por la información con el == en la parte CUANDO. No lo sabia. Con COALESCE también funciona bien. No he pensado que hay tantas posibilidades de hacer eso.
meni
15

Hay muchas soluciones, pero ninguna cubre por qué la declaración original no funciona.

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

Después del cuándo, hay una comprobación de igualdad, que debería ser verdadera o falsa.

Si una o ambas partes de una comparación es nula, el resultado de la comparación será DESCONOCIDO, que se trata como falso en una estructura de caso. Ver: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Para evitar esto, la fusión es la mejor manera.

kamahl
fuente
11

Dada su consulta, también puede hacer esto:

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person
Ian Jacobs
fuente
2
Esto agrega un espacio redundante cuando last_name es nulo, que es lo que el OP intentaba evitar.
Marcelo Cantos
66
Mejor uso ISNULL(' '+ last_name, '')para evitar el espacio redundante.
Prutswonder
Sí, me di cuenta después de que publiqué. Pensé que lo dejaría ya que resolvió la parte con la que estaba teniendo problemas.
Ian Jacobs
7

El problema es que nulo no se considera igual a sí mismo, por lo tanto, la cláusula nunca coincide.

Debe verificar nulo explícitamente:

SELECT CASE WHEN last_name is NULL THEN first_name ELSE first_name + ' ' + last_name
b0fh
fuente
5

tratar:

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

Esto agrega el espacio al apellido, si es nulo, todo el espacio + apellido va a NULL y solo obtienes un nombre, de lo contrario obtienes un primer nombre + espacio + apellido.

esto funcionará mientras se establezca la configuración predeterminada para la concatenación con cadenas nulas:

SET CONCAT_NULL_YIELDS_NULL ON 

Esto no debería ser una preocupación ya que el OFFmodo desaparecerá en futuras versiones de SQL Server

KM.
fuente
4

El problema es que NULL no se considera igual a nada, ni siquiera a sí mismo, pero lo extraño es que tampoco es igual a sí mismo.

Considere las siguientes declaraciones (que por cierto es ilegal en SQL Server T-SQL pero es válido en My-SQL, sin embargo, esto es lo que ANSI define para nulo, y se puede verificar incluso en SQL Server mediante el uso de declaraciones de casos, etc.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Por lo tanto, no hay una respuesta verdadera / falsa a la pregunta, sino que la respuesta también es nula.

Esto tiene muchas implicaciones, por ejemplo en

  1. Sentencias CASE, en las cuales cualquier valor nulo siempre usará la cláusula ELSE a menos que use explícitamente la condición WHEN IS NULL ( NO la WHEN NULLcondición )
  2. Concatenación de cadenas, como
    SELECT a + NULL -- Results in NULL
  3. En una cláusula WHERE IN o WHERE NOT IN, como si quisiera resultados correctos, asegúrese de que en la subconsulta correlacionada se filtren los valores nulos.

Se puede anular este comportamiento en SQL Server especificando SET ANSI_NULLS OFF , sin embargo, esto NO se recomienda y no debe hacerse, ya que puede causar muchos problemas, simplemente debido a la desviación del estándar.

(Como nota al margen, en My-SQL hay una opción para usar un operador especial <=>para la comparación nula).

En comparación, en los lenguajes de programación generales, nulo se trata como un valor regular y es igual a sí mismo, sin embargo, es el valor NAN que tampoco es igual a sí mismo, pero al menos devuelve 'falso' al compararlo consigo mismo (y cuando verifica que no sea igual, diferentes lenguajes de programación tienen implementaciones diferentes).

Sin embargo, tenga en cuenta que en los lenguajes básicos (es decir, VB, etc.) no hay una palabra clave 'nula' y en su lugar se usa la palabra clave 'Nada', que no se puede usar en comparación directa y en su lugar se debe usar 'IS' como en SQL, sin embargo, de hecho es igual a sí mismo (cuando se usan comparaciones indirectas).

Yoel Halb
fuente
1
"Lo extraño es que tampoco es igual a sí mismo". => esto no es extraño teniendo en cuenta que nulo en SQL tiene el significado 'desconocido'. Algo desconocido, lo más probable es que no sea lo mismo que otra cosa que no se conoce.
JDC
1
CASE  
    WHEN last_name IS null THEN '' 
    ELSE ' ' + last_name 
END
Linda
fuente
1

Cuando te sientas frustrado intentando esto:

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

Prueba este en su lugar:

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

LEN(ISNULL(last_Name,''))mide el número de caracteres en esa columna, que será cero si está vacío o NULL, por WHEN 0 THENlo tanto, se evaluará como verdadero y devolverá el '' como se esperaba.

Espero que esta sea una alternativa útil.

He incluido este caso de prueba para SQL Server 2008 y superior:

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
Chef Slagle
fuente
2
¿Estás seguro de que funciona? La mayoría de las funciones devuelven NULL cuando reciben una entrada NULL y mis pruebas confirman que eso también es cierto para LEN (), es decir, LEN (NULL) devuelve NULL, no 0.
Jason Musgrove
Pokechu22 es correcto, y este es el script corregido: CASE LEN (ISNULL (last_Name, '')) WHEN 0 THEN '' ELSE '' + last_name END AS newlastName
Chef Slagle
0

Jason detectó un error, así que esto funciona ...

¿Alguien puede confirmar las otras versiones de la plataforma?
Servidor 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

Oráculo:

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

Puedes usar la función IsNull

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

si una columna es nula obtendría un carácter vacío

Compatible con Microsoft SQL Server 2008+

Molem
fuente
0

Use la función CONCAT disponible en SQL Server 2012 en adelante.

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

Intenté lanzar una cadena y probar una cadena de longitud cero y funcionó.

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

NULL no es igual a nada. La declaración del caso básicamente dice cuando el valor = NULL ... nunca golpeará.
También hay varios procedimientos almacenados del sistema que están escritos incorrectamente con su sintaxis. Consulte sp_addpullsubscription_agent y sp_who2.
Ojalá supiera cómo notificar a Microsoft sobre esos errores, ya que no puedo cambiar los procesos almacenados del sistema.

Joshua Walker
fuente