Diferente <>! = Operador em NULL

271

Alguém poderia explicar o seguinte comportamento no 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
fonte

Respostas:

309

<>é o padrão SQL-92; !=é o seu equivalente. Ambos avaliam valores, o que NULLnão é - NULLé um espaço reservado para dizer que há ausência de valor.

É por isso que você só pode usar IS NULL/ IS NOT NULLcomo predicado para essas situações.

Esse comportamento não é específico para o SQL Server. Todos os dialetos SQL compatíveis com os padrões funcionam da mesma maneira.

Nota : Para comparar se seu valor não é nulo , use IS NOT NULL, enquanto para comparar com valor não nulo , use <> 'YOUR_VALUE'. Não posso dizer se meu valor é igual ou não a NULL, mas posso dizer se meu valor é NULL ou NOT NULL. Posso comparar se meu valor é algo diferente de NULL.

Pôneis OMG
fonte
4
Na verdade, eu acredito que é <>que está no 92 especificação, mas a maioria dos fornecedores de suporte !=e / ou está incluída em uma especificação mais tarde como 99 ou 03.
Thomas
2
@ Thomas: O Oracle não suportou !=até ~ 9i, pelo que entendi, o que trouxe muita sintaxe ANSI-92. Minha crença é que o MySQL é semelhante, iniciando o suporte na versão 4.x.
OMG Ponies
Isso parece sugerir que !=poderia ter sido incluído em uma especificação posterior como uma alternativa a <>. Não tenho minhas mãos em novas especificações, então não posso dizer com certeza.
Thomas
2
É o resultado de WHERE MyColumn != NULLou WHERE MyColumn = NULLdeterminista? Ou, em outras palavras, é garantido o retorno sempre de 0 linhas, independentemente de MyColumnser nulo no banco de dados ou não?
Slauma
11
Também deve ser observado que, porque !=apenas avalia valores, fazer algo como WHERE MyColumn != 'somevalue'não retornará os registros NULL.
jsumrall
88

NULL não tem valor e, portanto, não pode ser comparado usando os operadores de valor escalar.

Em outras palavras, nenhum valor pode ser igual a (ou não) NULL porque NULL não tem valor.

Portanto, o SQL possui predicados especiais IS NULL e IS NOT NULL para lidar com NULL.

Barry Brown
fonte
3
+1. E, ao contrário da instrução OP, isso não é "Microsoft SQL". A lógica trinária é definida no SQL Standard e o MS, neste ponto, adere ao padrão.
TomTom
6
Eu não estava sugerindo que este é um comportamento exclusivo da Microsoft. Eu estava simplesmente afirmando que o observei no Microsoft SQL Server.
precisa saber é o seguinte
13
Fora de interesse, existem situações em que esse comportamento (esperado) é útil? Parece-me que 'a' != nullNÃO devolver um valor ( true/ 1) é contra-intuitivo e me pega de vez em quando! Eu teria pensado que "algum valor comparado a nenhum valor" sempre seria "não igual", mas talvez seja apenas eu?!?
DarthPablo
1
Eu acho interessante que as pessoas descrevam NULL como ' sem valor '. Semelhante, então, dizer que o número 1 'tem um valor' quando na verdade é um valor. Mas NULL representa não-valor .. #
systemmaddict 27/06
Como solução alternativa manual, você pode SELECT * FROM MyTable WHERE coalesce(MyColumn, 'x') <> 'x'atribuir uma constante se for um valor NULL, desde que você forneça um tipo de dados apropriado para o valor sentinela x (nesse caso, uma string / char). Essa é a sintaxe do TSQL, mas o Oracle e outros mecanismos têm recursos semelhantes.
systemaddict
26

Observe que esse comportamento é o comportamento padrão (ANSI).

Se vocês:

 SET ANSI_NULLS OFF

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

Você obterá resultados diferentes.

SET ANSI_NULLS OFF aparentemente vai desaparecer no futuro ...

Cade Roux
fonte
8
+1 ... não em breve. Agora, quando posso obter NULLs "duplicados" em um índice? :(
Você pode obter nulos duplicados em um índice SQL Server, adicionando uma cláusula WHERE em um índice filtrado (por exemplo create unique index UK_MyTable on MyTable (Column) where Column is not null): msdn.microsoft.com/en-us/library/cc280372.aspx
Anthony Mills
3
Nota dos documentos: Quando SET ANSI_NULLSestá DESATIVADO, os operadores de comparação Igual a (=) e Diferente de (<>) não seguem o padrão ISO. Uma instrução SELECT que usa WHERE column_name = NULLretorna as linhas que possuem valores nulos em column_name. Uma instrução SELECT que usa WHERE column_name <> NULLretorna as linhas que possuem valores não nulos na coluna. Além disso, uma instrução SELECT que usa WHERE column_name <> XYZ_valueretorna todas as linhas que não são XYZ_value e que não são NULL. IMHO, esta última afirmação parece um pouco estranha na exclusão de valores nulos dos resultados!
DarthPablo
4
Nota importante do msdn doc : Em uma versão futura do SQL Server [mais recente que 2014], o ANSI_NULLS sempre estará LIGADO e qualquer aplicativo que defina explicitamente a opção como DESLIGADO gerará um erro. Evite usar esse recurso em novos trabalhos de desenvolvimento e planeje modificar os aplicativos que atualmente usam esse recurso.
Otiel
7

No SQL, qualquer coisa que você avalie / calcule com NULLresultados em DESCONHECIDO

É por isso que SELECT * FROM MyTable WHERE MyColumn != NULLou SELECT * FROM MyTable WHERE MyColumn <> NULLfornece 0 resultados.

Para fornecer uma verificação de NULLvalores, a função isNull é fornecida.

Além disso, você pode usar o ISoperador como usou na terceira consulta.

Espero que isto ajude.

Mahendra Liya
fonte
"No SQL, qualquer coisa que você avalie / calcule com resultados NULL em 'NULL'" - incorreto. O resultado que você quer dizer é DESCONHECIDO.
precisa saber é o seguinte
@MahendraLiya, a função isNull não é fornecida para verificar NULLS, mas " Substitui NULL pelo valor de substituição especificado ". Você deve usar IS NULL ou IS NOT NULL em vez de ISNULL, o que é uma coisa diferente.
Reversed Engineer
7

O único teste para NULL é IS NULL ou NOT NULL. Testar a igualdade não faz sentido, porque, por definição, não se sabe qual é o valor.

Aqui está um artigo da Wikipedia para ler:

https://en.wikipedia.org/wiki/Null_(SQL)

dkretz
fonte
6

Nós usamos

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

para retornar todas as linhas em que MyColumn é NULL ou todas as linhas em que MyColumn é uma sequência vazia. Para muitos "usuários finais", o problema de seqüência de caracteres NULL vs. vazio é uma distinção sem necessidade e ponto de confusão.

Jeff Mergler
fonte
5

Eu simplesmente não vejo a razão funcional e perfeita para que os nulos não sejam comparáveis ​​a outros valores ou outros nulos, porque podemos compará-lo claramente e dizer que eles são iguais ou não em nosso contexto. É engraçado. Só por causa de algumas conclusões lógicas e consistência, precisamos nos preocupar constantemente com isso. Não é funcional, torne-o mais funcional e deixe que filósofos e cientistas concluam se é consistente ou não e mantém a "lógica universal". :) Alguém pode dizer que é por causa de índices ou qualquer outra coisa, duvido que essas coisas não pudessem ser feitas para suportar nulos iguais aos valores. É o mesmo que comparar dois copos vazios, um é copo de vinho e outro é copo de cerveja; não estamos comparando os tipos de objetos, mas os valores que eles contêm, o mesmo que você pode comparar int e varchar, com nulo ' é ainda mais fácil, não é nada e o que dois nada têm em comum, eles são os mesmos, claramente comparáveis ​​por mim e por todos os outros que escrevem sql, porque estamos constantemente quebrando essa lógica, comparando-os de maneiras estranhas por causa de alguns padrões ANSI. Por que não usar a energia do computador para fazer isso por nós e duvido que isso acelere as coisas se tudo relacionado for construído com isso em mente. "Não é nulo, não é nada", não é maçã, é apfel, vamos lá ... Funcionalmente é seu amigo e também há lógica aqui. No final, a única coisa que importa é a funcionalidade e o uso de nulos dessa maneira traz mais ou menos funcionalidade e facilidade de uso. É mais útil? porque estamos constantemente quebrando essa lógica, comparando-os de maneiras estranhas por causa de alguns padrões ANSI. Por que não usar a energia do computador para fazer isso por nós e duvido que isso acelere as coisas se tudo relacionado for construído com isso em mente. "Não é nulo, não é nada", não é maçã, é apfel, vamos lá ... Funcionalmente é seu amigo e também há lógica aqui. No final, a única coisa que importa é a funcionalidade e o uso de nulos dessa maneira traz mais ou menos funcionalidade e facilidade de uso. É mais útil? porque estamos constantemente quebrando essa lógica, comparando-os de maneiras estranhas por causa de alguns padrões ANSI. Por que não usar a energia do computador para fazer isso por nós e duvido que isso acelere as coisas se tudo relacionado for construído com isso em mente. "Não é nulo, não é nada", não é maçã, é apfel, vamos lá ... Funcionalmente é seu amigo e também há lógica aqui. No final, a única coisa que importa é a funcionalidade e o uso de nulos dessa maneira traz mais ou menos funcionalidade e facilidade de uso. É mais útil? s não maçã é apfel, vamos lá ... Funcionalmente é seu amigo e também há lógica aqui. No final, a única coisa que importa é a funcionalidade e o uso de nulos dessa maneira traz mais ou menos funcionalidade e facilidade de uso. É mais útil? s não maçã é apfel, vamos lá ... Funcionalmente é seu amigo e também há lógica aqui. No final, a única coisa que importa é a funcionalidade e o uso de nulos dessa maneira traz mais ou menos funcionalidade e facilidade de uso. É mais útil?

Considere este código:

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

Quantos de vocês sabem o que esse código retornará? Com ou sem NÃO, retorna 0. Para mim, isso não é funcional e é confuso. No c #, tudo é como deveria ser, as operações de comparação retornam valor, logicamente isso também produz valor, porque, se isso não acontecesse, não havia nada para comparar (exceto nada :):). Eles apenas "disseram": qualquer coisa comparada com "retornos" nulos 0 e que cria muitas soluções alternativas e dores de cabeça.

Este é o código que me trouxe aqui:

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

Eu só preciso comparar se dois campos (em onde) têm valores diferentes, eu poderia usar a função, mas ...

Hrvoje Batrnek
fonte
4

NULL Não pode ser comparado a nenhum valor usando os operadores de comparação. NULL = NULL é falso. Nulo não é um valor. O operador IS é especialmente projetado para lidar com comparações NULL.

Vincent Ramdhanie
fonte
5
Eu sempre gostei de pessoas confusas quando às vezes uso null = nullonde alguém pode usar 1=0em alguma consulta ad-hoc. E se eles se queixam, eu mudar para null != null:)
Sweko
8
"NULL = NULL é falso" Isso não é verdade. NULL = NULL é avaliado como desconhecido e não falso.
Nvogel
@portas isso é verdade, mas eu quis dizer que, de forma condicional, não será avaliada como verdadeira.
Vincent Ramdhanie
@VincentRamdhanie nem como falso; de fato, no postgres ele será avaliado como NULL
Pere
2

Pergunta antiga, mas o seguinte pode oferecer mais detalhes.

nullrepresenta nenhum valor ou um valor desconhecido. Não especifica por que não há valor, o que pode levar a alguma ambiguidade.

Suponha que você execute uma consulta como esta:

SELECT *
FROM orders
WHERE delivered=ordered;

isto é, você está procurando linhas nas quais as datas orderede deliveredsão iguais.

O que é esperado quando uma ou ambas as colunas são nulas?

Como pelo menos uma das datas é desconhecida, você não pode esperar dizer que as duas datas são iguais. Este também é o caso quando as duas datas são desconhecidas: como podem ser as mesmas se nem sabemos o que são?

Por esse motivo, qualquer expressão que trate nullcomo um valor deve falhar. Nesse caso, não corresponderá. Este também é o caso se você tentar o seguinte:

SELECT *
FROM orders
WHERE delivered<>ordered;

Novamente, como podemos dizer que dois valores não são os mesmos se não sabemos o que são.

SQL tem um teste específico para valores ausentes:

IS NULL

Especificamente, não está comparando valores, mas procura valores ausentes .

Finalmente, no que diz respeito ao !=operador, tanto quanto sei, ele não está de fato em nenhuma das normas, mas é amplamente suportado. Foi adicionado para fazer com que programadores de alguns idiomas se sintam mais à vontade. Francamente, se um programador tem dificuldade em lembrar qual idioma está usando, ele começa mal.

Manngo
fonte
Essa é a mesma lógica "absurda" que o @Hove está descrevendo em sua resposta. A verdade é que, neste contexto, não há necessidade de parafernália extra; pode-se facilmente assumir que, quando comparamos algo a um, NULLqueremos dizer que estamos comparando um valor a 'ter um NULLvalor', não o valor ao "valor indeterminado que o subjacente NULLestá tendo? mas que não sabemos ", que obviamente não poderemos saber. Isso realmente facilitaria as coisas.
Pere
@Pere Eu não diria que é estritamente "sem sentido", e não tenho certeza de que escrever IS NULLseja muito mais árduo do que escrever = NULL. Eu acho que seria mais consistente se WHERE columnA = columnBtivesse a mesma interpretação que WHERE columnA = NULL, em vez de tratar a última como um caso especial. Lembre-se que nãoNULL é um valor. Nas linguagens de programação em que é legítimo testá- lo, é porque tem um significado diferente; não representa algo desconhecido, mas uma redefinição deliberada de um valor. Não é assim com o SQL. variable == nullnull
Manngo 12/04/19
É por isso que coloquei entre aspas, @Mangoo;) (e também "lógica"). Não fique bravo comigo; Eu estava falando sobre o "raciocínio" ANSI, não sobre sua explicação. Concordo que não há sobrecarga entre IS NULLAND =NULLno seu exemplo mais recente. Mas dê uma olhada no último de Hover. Estou cansado de experimentar isso de novo e de novo, tendo que fazer muitas coisas desnecessárias? verificação extra ...
Pere
1

Gostaria de sugerir esse código que fiz para descobrir se há uma alteração em um valor, isendo o novo valor e do antigo (embora a ordem não importe). Nesse caso, uma mudança de valor para nulo ou vice-versa é uma mudança, mas de nulo para nulo não é (é claro, de valor para outro valor é uma mudança, mas de valor para o mesmo não é).

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

Para usar esta função, você pode

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

Os resultados são:

---- 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

O uso de sql_variant o torna compatível com vários tipos

Yariv de Botton
fonte