Cláusula CASE T-SQL: Como especificar WHEN NULL

227

Eu escrevi uma instrução T-SQL semelhante a esta (a original parece diferente, mas quero dar um exemplo fácil aqui):

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

Esta instrução não possui nenhum erro de sintaxe, mas a cláusula case escolhe sempre a parte ELSE - também se o last_name for nulo. Mas por que?

O que eu quero fazer é unir first_name e last_name, mas se last_name for nulo, o nome inteiro se tornará nulo:

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

Você sabe onde está o problema?

meni
fonte

Respostas:

369
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END
Marcelo Cantos
fonte
4
A sugestão do COALESCE de Lutero é melhor do que minha resposta. É marginalmente menos eficiente, mas muito mais elegante.
Marcelo Cantos
Manipulá-lo com algo assim pode ser útil ou algo parecido: COALESCE (last_name, '') A declaração de caso, embora concisa, é imho menos sustentável do que COALESCE em consultas grandes. No final, você tem o mesmo resultado. Se você precisar otimizar, verifique os planos de execução, mas não notei muita diferença.
Anthony Mason
1
COALESCEbasicamente traduz internamente para CASE. O problema com o CASE é que last_nameé avaliado duas vezes e pode levar a outros efeitos colaterais - consulte este excelente artigo . Para resolver o que foi solicitado, prefiro ISNULL(' '+ last_name, '')mencionar o comentário abaixo.
precisa saber é o seguinte
41

A parte WHEN é comparada com ==, mas você realmente não pode comparar com NULL. Experimentar

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

em vez disso ou COALESCE:

COALESCE(' '+last_name,'')

('' + last_name é NULL quando last_name é NULL, portanto, ele deve retornar '' nesse caso)

Mainframe nórdico
fonte
Ok, obrigado pelas informações com o == na parte WHEN. Eu não sabia disso. Com o COALESCE, ele também funciona bem. Não pensei que há tantas possibilidades para fazer isso.
meni
15

Existem muitas soluções, mas nenhuma cobre por que a declaração original não funciona.

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

Após o quando, há uma verificação de igualdade, que deve ser verdadeira ou falsa.

Se uma ou ambas as partes de uma comparação for nula, o resultado da comparação será DESCONHECIDO, que é tratado como falso em uma estrutura de caso. Veja: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/

Para evitar isso, Coalesce é o melhor caminho.

kamahl
fonte
11

Dada sua consulta, você também pode fazer isso:

SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person
Ian Jacobs
fonte
2
Isso adiciona um espaço redundante quando last_name é nulo, o que o OP estava tentando evitar.
Marcelo Cantos
6
Melhor uso ISNULL(' '+ last_name, '')para evitar o espaço redundante.
Prutswonder
Sim percebi depois que eu postei. Achei que eu deixaria, já que resolveu a parte com a qual ele estava tendo problemas.
11118 Ian
7

O problema é que nulo não é considerado igual a si mesmo, portanto a cláusula nunca corresponde.

Você precisa verificar se há nulo explicitamente:

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

experimentar:

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

Isso adiciona o espaço ao sobrenome, se for nulo, todo o espaço + sobrenome passa a NULL e você obtém apenas um primeiro nome; caso contrário, obtém um primeiro + espaço + sobrenome.

isso funcionará desde que a configuração padrão para concatenação com cadeias nulas seja definida:

SET CONCAT_NULL_YIELDS_NULL ON 

isso não deve ser uma preocupação, pois o OFFmodo está desaparecendo em versões futuras do SQl Server

KM.
fonte
4

A questão é que NULL não é considerado igual a qualquer coisa, mesmo que não seja ela mesma, mas a parte estranha é que também não é igual a ela mesma.

Considere as seguintes instruções (que são ilegais no BT-SQL Server T-SQL, mas são válidas no My-SQL, no entanto, é isso que o ANSI define como nulo e pode ser verificado mesmo no SQL Server usando instruções de caso etc.)

SELECT NULL = NULL -- Results in NULL

SELECT NULL <> NULL -- Results in NULL

Portanto, não há resposta verdadeira / falsa para a pergunta, mas a resposta também é nula.

Isso tem muitas implicações, por exemplo, em

  1. Instruções CASE, nas quais qualquer valor nulo sempre usará a cláusula ELSE, a menos que você use explicitamente a condição WHEN IS NULL ( NÃO a WHEN NULLcondição )
  2. Concatenação de string, como
    SELECT a + NULL -- Results in NULL
  3. Em uma cláusula WHERE IN ou WHERE NOT IN, como se você deseja resultados corretos, certifique-se de que na subconsulta correlacionada filtre quaisquer valores nulos.

Pode-se substituir esse comportamento no SQL Server, especificando SET ANSI_NULLS OFF, no entanto, isso NÃO é recomendado e não deve ser feito, pois pode causar muitos problemas, simplesmente devido ao desvio do padrão.

(Como uma observação lateral, no My-SQL há uma opção para usar um operador especial <=>para comparação nula.)

Em comparação, nas linguagens de programação gerais, nulo é tratado é um valor regular e é igual a si mesmo; no entanto, é o valor NAN que também não é igual a si mesmo, mas pelo menos retorna 'false' ao compará-lo a si mesmo (e ao verificar se não é igual a diferentes linguagens de programação, tem implementações diferentes).

Observe, no entanto, que nas linguagens Básicas (por exemplo, VB etc.) não existe uma palavra-chave 'nula' e, em vez disso, é usada a palavra-chave 'Nada', que não pode ser usada em comparação direta e, em vez disso, é necessário usar 'IS' como no SQL, no entanto, é de fato igual a si mesmo (ao usar comparações indiretas).

yoel halb
fonte
1
"A parte estranha é que também não é igual a si mesma." => isso não é estranho, considerando que nulo no SQL tem o significado 'desconhecido'. Algo desconhecido, provavelmente não é o mesmo que algo desconhecido.
JDC
1
CASE  
    WHEN last_name IS null THEN '' 
    ELSE ' ' + last_name 
END
Linda
fonte
1

Quando você fica frustrado tentando isso:

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

Experimente este:

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

LEN(ISNULL(last_Name,''))mede o número de caracteres nessa coluna, que será zero se estiver vazio ou NULL; portanto, WHEN 0 THENserá avaliado como true e retornará o '' conforme o esperado.

Espero que seja uma alternativa útil.

Eu incluí este caso de teste para o sql server 2008 e acima:

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
fonte
2
Tem certeza de que funciona? A maioria das funções retornam NULL quando é dada uma entrada de NULL e meus testes confirmam que também é verdade para LEN (), ou seja, LEN (NULL) retorna NULL, não 0.
Jason Musgrove
Pokechu22 está correto e este é o script corrigido: CASE LEN (ISNULL (last_Name, '')) WHEN 0 THEN '' ELSE '' + last_name END AS newlastName
Chef Slagle
0

Jason pegou um erro, então isso funciona ...

Alguém pode confirmar as outras versões da 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
fonte
0

Você pode usar a função IsNull

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

se uma coluna for nula, você receberá um caractere vazio

Compatível com Microsoft SQL Server 2008+

Molem
fonte
0

Use a função CONCAT disponível no SQL Server 2012 em diante.

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

Tentei converter para uma string e testar uma string de comprimento zero e funcionou.

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

NULL não é igual a nada. A declaração de caso está basicamente dizendo quando o valor = NULL .. nunca será atingido.
Existem também vários procedimentos armazenados do sistema que são gravados incorretamente com sua sintaxe. Consulte sp_addpullsubscription_agent e sp_who2.
Gostaria de saber como notificar a Microsoft sobre esses erros, pois não consigo alterar os procs armazenados no sistema.

Joshua Walker
fonte