Esse problema surgiu quando obtive diferentes contagens de registros para o que eu pensava serem consultas idênticas, uma usando uma not in
where
restrição e a outra a left join
. A tabela na not in
restrição tinha um valor nulo (dados inválidos) que fazia com que a consulta retornasse uma contagem de 0 registros. Eu meio que entendo o porquê, mas eu poderia usar alguma ajuda para entender completamente o conceito.
Para simplificar, por que a consulta A retorna um resultado, mas B não?
A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)
Isso foi no SQL Server 2005. Também descobri que a chamada set ansi_nulls off
faz com que B retorne um resultado.
NOT IN
para uma série de<> and
mudanças altera o comportamento semântico de não neste conjunto para outra coisa?SELECT 1 WHERE NULL NOT IN (SELECT 1 WHERE 1=0);
gera uma linha em vez do conjunto de resultados vazio que eu esperava.Sempre que você usa NULL, está realmente lidando com uma lógica de três valores.
Sua primeira consulta retorna resultados à medida que a cláusula WHERE é avaliada para:
O segundo:
O DESCONHECIDO não é o mesmo que FALSO, você pode testá-lo facilmente chamando:
Ambas as consultas não fornecerão resultados
Se UNKNOWN fosse o mesmo que FALSE, assumindo que a primeira consulta lhe forneceria FALSE, a segunda teria que ser avaliada como TRUE, pois teria sido a mesma que NOT (FALSE).
Esse não é o caso.
Há um artigo muito bom sobre esse assunto no SqlServerCentral .
Toda a questão dos NULLs e da lógica de três valores pode ser um pouco confusa no começo, mas é essencial entender para escrever consultas corretas no TSQL
Outro artigo que eu recomendaria é SQL Aggregate Functions e NULL .
fonte
NOT IN
retorna 0 registros quando comparado a um valor desconhecidoComo
NULL
é desconhecido, umaNOT IN
consulta que contenha aNULL
ouNULL
s na lista de valores possíveis sempre retornará0
registros, pois não há como garantir que oNULL
valor não seja o valor que está sendo testado.fonte
Comparar com nulo é indefinido, a menos que você use IS NULL.
Portanto, ao comparar 3 com NULL (consulta A), ele retorna indefinido.
Ou seja, SELECT 'true' em que 3 pol (1,2, nulo) e SELECT 'true' em que 3 pol (1,2, nulo)
produzirá o mesmo resultado, pois NOT (UNDEFINED) ainda está indefinido, mas não é TRUE
fonte
O título desta pergunta no momento da redação deste artigo é
A partir do texto da pergunta, parece que o problema estava ocorrendo em uma
SELECT
consulta SQL DML , em vez de uma DDL SQLCONSTRAINT
.No entanto, especialmente dada a redação do título, quero salientar que algumas declarações feitas aqui são declarações potencialmente enganosas, aquelas do tipo (parafraseando)
Embora este seja o caso do SQL DML, ao considerar restrições, o efeito é diferente.
Considere esta tabela muito simples com duas restrições tiradas diretamente dos predicados da pergunta (e abordadas em uma excelente resposta de @Brannon):
De acordo com a resposta de @ Brannon, a primeira restrição (usando
IN
) é avaliada como VERDADEIRA e a segunda restrição (usandoNOT IN
) é avaliada como DESCONHECIDO. Contudo , a inserção foi bem-sucedida! Portanto, neste caso, não é estritamente correto dizer "você não recebe nenhuma linha" porque, de fato, temos uma linha inserida como resultado.O efeito acima é de fato o correto em relação ao padrão SQL-92. Compare e contraste a seção a seguir da especificação SQL-92
Em outras palavras:
No SQL DML, as linhas são removidas do resultado quando
WHERE
avalia como DESCONHECIDO, porque não atende à condição "é verdadeira".Em SQL DDL (ou seja, restrições), as linhas não são removidos a partir do resultado quando avaliam a desconhecida, dado que não satisfazem a condição "não é falsa".
Embora os efeitos no SQL DML e no SQL DDL possam parecer contraditórios, existe uma razão prática para dar aos resultados UNKNOWN o 'benefício da dúvida', permitindo que eles satisfaçam uma restrição (mais corretamente, permitindo que eles não deixem de satisfazer uma restrição). : sem esse comportamento, todas as restrições teriam que lidar explicitamente com nulos e isso seria muito insatisfatório do ponto de vista do design da linguagem (para não mencionar, um problema para os codificadores!)
ps Se você está achando tão desafiador seguir uma lógica como "desconhecido não falha em satisfazer uma restrição" como eu estou escrevendo, considere que você pode dispensar tudo isso simplesmente evitando colunas anuláveis no DDL do SQL e qualquer coisa no SQL DML que produz nulos (por exemplo, junções externas)!
fonte
NOT IN (subquery)
nulo pode dar resultados inesperados, é tentador evitarIN (subquery)
completamente e sempre usarNOT EXISTS (subquery)
(como eu fiz uma vez!), Porque parece que ele sempre lida com o nulo corretamente. No entanto, existem casos em queNOT IN (subquery)
fornece o resultado esperado, enquantoNOT EXISTS (subquery)
fornece resultados inesperados! Ainda posso escrever isso se conseguir encontrar minhas anotações sobre o assunto (preciso de anotações porque não é intuitivo!) A conclusão é a mesma: evite valores nulos!TRUE
,FALSE
eUNKNOWN
. Suponho que a versão 4.10 poderia ter lido: "Uma restrição de verificação de tabela é satisfeita se, e somente se, a condição de pesquisa especificada for VERDADEIRA ou DESCONHECIDA para cada linha de uma tabela" - observe minha alteração no final da frase - que você omitiu - - de "for any" para "for all". Sinto a necessidade de capitalizar os valores lógicos porque o significado de 'verdadeiro' e 'falso' na linguagem natural deve certamente se referir à lógica clássica de dois valores.CREATE TABLE T ( a INT NOT NULL UNIQUE, b INT CHECK( a = b ) );
- a intenção aqui é queb
deve ser iguala
ou ser nulo. Se uma restrição tiver que resultar em TRUE para ser satisfeita, precisaremos alterar a restrição para lidar com nulos explicitamente, por exemploCHECK( a = b OR b IS NULL )
. Assim, toda restrição precisaria ter...OR IS NULL
lógica adicionada pelo usuário para cada coluna anulável envolvida: mais complexidade, mais bugs quando eles se esquecessem de fazê-lo, etc. Então, acho que o comitê de padrões SQL estava apenas tentando ser pragmático.Em A, 3 é testado quanto à igualdade em relação a cada membro do conjunto, produzindo (FALSE, FALSE, TRUE, UNKNOWN). Como um dos elementos é VERDADEIRO, a condição é VERDADEIRA. (Também é possível que ocorra um curto-circuito aqui, portanto, ele pára assim que atinge o primeiro TRUE e nunca avalia 3 = NULL.)
Em B, acho que está avaliando a condição como NOT (3 in (1,2, null)). Teste 3 para igualdade em relação aos rendimentos definidos (FALSE, FALSE, UNKNOWN), que é agregado a UNKNOWN. NOT (DESCONHECIDO) gera DESCONHECIDO. Portanto, no geral, a verdade da condição é desconhecida, que no final é essencialmente tratada como FALSA.
fonte
Pode-se concluir a partir de respostas aqui que
NOT IN (subquery)
não tratam nulos corretamente e devem ser evitadas em favor deNOT EXISTS
. No entanto, essa conclusão pode ser prematura. No cenário a seguir, creditado a Chris Date (Database Programming and Design, Vol. 2, n. 9, setembro de 1989), éNOT IN
que lida com nulos corretamente e retorna o resultado correto, em vez deNOT EXISTS
.Considere uma tabela
sp
para representar os fornecedores (sno
) que são conhecidos por fornecer peças (pno
) em quantidade (qty
). A tabela atualmente possui os seguintes valores:Observe que a quantidade é anulável, ou seja, para poder registrar o fato de que um fornecedor é conhecido por fornecer peças, mesmo que não seja conhecido em qual quantidade.
A tarefa é encontrar os fornecedores que sabem o número da peça de fornecimento 'P1', mas não em quantidades de 1000.
Os seguintes usos usam
NOT IN
para identificar corretamente apenas o fornecedor 'S2':No entanto, a consulta abaixo usa a mesma estrutura geral, mas com,
NOT EXISTS
mas inclui incorretamente o fornecedor 'S1' no resultado (ou seja, para o qual a quantidade é nula):Portanto,
NOT EXISTS
não é a bala de prata que pode ter aparecido!Obviamente, a fonte do problema é a presença de nulos; portanto, a solução 'real' é eliminar esses nulos.
Isso pode ser alcançado (entre outros projetos possíveis) usando duas tabelas:
sp
fornecedores conhecidos por fornecer peçasspq
fornecedores conhecidos por fornecer peças em quantidades conhecidasobservando que provavelmente deve haver uma restrição de chave estrangeira em que as
spq
referênciassp
.O resultado pode ser obtido usando o operador relacional 'menos' (sendo a
EXCEPT
palavra - chave no SQL padrão), por exemplofonte
Nulo significa e ausência de dados, isto é, é desconhecido, não um valor de dados de nada. É muito fácil para as pessoas com experiência em programação confundirem isso, porque nas linguagens do tipo C, ao usar ponteiros nulos, de fato, nada é.
Portanto, no primeiro caso, 3 está realmente no conjunto de (1,2,3, nulo), então true é retornado
No segundo, você pode reduzi-lo para
selecione 'true' onde 3 não estão em (nulo)
Portanto, nada é retornado porque o analisador não sabe nada sobre o conjunto com o qual você está comparando - não é um conjunto vazio, mas um conjunto desconhecido. Usar (1, 2, nulo) não ajuda porque o conjunto (1,2) é obviamente falso, mas você está fazendo isso contra desconhecido, o que é desconhecido.
fonte
Se você deseja filtrar com NOT IN para uma subconsulta contendo NULLs, verifique se não é nulo
fonte
isto é para o menino:
isso funciona independentemente das configurações da ansi
fonte
SQL usa lógica de três valores para valores verdadeiros. A
IN
consulta produz o resultado esperado:Mas adicionar a
NOT
não inverte os resultados:Isso ocorre porque a consulta acima é equivalente ao seguinte:
Aqui está como a cláusula where é avaliada:
Notar que:
NULL
rendimentosUNKNOWN
OR
expressão em que nenhum dos operandos estáTRUE
e pelo menos um operando éUNKNOWN
produzidaUNKNOWN
( ref )NOT
deUNKNOWN
rendimentosUNKNOWN
( ref )Você pode estender o exemplo acima para mais de dois valores (por exemplo, NULL, 1 e 2), mas o resultado será o mesmo: se um dos valores for
NULL
, nenhuma linha corresponderá.fonte
isso também pode ser útil para saber a diferença lógica entre associação, existe e em http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
fonte