O SQL nesta questão é para Oracle. Mas você pode simplesmente remover FROM dual
para que funcione no SQL Server (que retorna o mesmo resultado do Oracle).
Tenho o seguinte SQL:
SELECT 1
FROM dual
WHERE NULL IN (SELECT 1 FROM dual WHERE 1 = 0);
SELECT 1
FROM dual
WHERE NULL NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
que retorna um conjunto vazio e 1 respectivamente. Quando substituímos algum outro valor por NULL
, obtemos exatamente o mesmo resultado.
SELECT 1
FROM dual
WHERE 33 IN (SELECT 1 FROM dual WHERE 1 = 0);
SELECT 1
FROM dual
WHERE 33 NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
Então, como a comparação entre NULL e o conjunto de resultados vazio funciona aqui?
Vamos ver o que acontece com essas duas condições:
como a subconsulta produz uma tabela vazia, podemos escrevê-los em pseudocódigo:
Então, o primeiro pergunta se algum valor (NULL) está em uma tabela vazia. O resultado de tal condição é sempre
FALSE
, independente de o valor ser nulo ou não, pois a tabela está vazia .Aplicando o mesmo raciocínio na segunda condição, é sempre
TRUE
, novamente independente do valor.É fácil confirmar no Postgres qual é o valor do resultado dessas condições, pois temos um tipo booleano. Veja dbfiddle.uk , onde o primeiro mostra
f
(FALSE) e o segundo mostrat
(TRUE).Como resultado do acima, quando você executa suas duas consultas, elas se tornam:
dando um resultado vazio (correto, já que a
WHERE
condição éFALSE
)e
dando uma linha (correta novamente).
Este é um problema com a chamada "lógica de três valores", que é exclusiva do SQL. Ao contrário de outras linguagens de programação que trabalham com
TRUE
eFALSE
, o SQL também possui UNKNOWN (NULL). E a maneira como os diferentes elementos do SQL tratam um UNKNOWN é inconsistente.ON
,WHERE
eHAVING
todos consideram UNKNOWN como FALSE. Um conjunto de resultados vazio é considerado NULL pelo SQL. Portanto, oNOT IN
está procurando uma declaração falsa e, como WHERE interpreta NULL como FALSE, você recebe sua saída. A comparação de dois valores NULL também retorna UNKNOWN.Por outro lado, as
CHECK
restrições consideram NULL como TRUE. Por exemplo, se você criar umaCHECK
restrição em vendas maiores que zero, NULL > 0 será avaliado como UNKNOWN, mas a regra é que uma restrição não deve ser avaliada como FALSE, então ela termina como TRUE. (editado com base no comentário de Lennart).Explicação detalhada:
(sintaxe do SQL Server)
Retorna um conjunto vazio e, como um conjunto de dados vazio é considerado NULL pelo SQL, o que você está fazendo é:
Para ser mais específico, o que você está comparando é uma tabela de todas as linhas = NULL
https://www.oreilly.com/library/view/sql-and-relational/9781449319724/ch12s07.html
If a row subquery evaluates to an empty table, that empty table is coerced to a row of all nulls.
Você também tem a parte matemática disso:
http://www.solving-math-problems.com/null-set-empty-set.html
Ao comparar dois NULLs, o resultado é sempre UNKNOWN, e as instruções WHERE consideram UNKNOWN como FALSE.
Portanto, em seu primeiro código:
Sua instrução WHERE está basicamente verificando se NULL existe dentro de um conjunto NULL e, como a comparação de NULL é UNKNOWN e UNKNOWN é considerada FALSE, você não obtém nenhum resultado.
Mas aqui:
Você está dizendo WHERE NULL não existe dentro de um conjunto NULL. Novamente, como UNKNOWN é considerado FALSE, você obtém uma saída.
Isso também explica por que quando você comparou:
Você recebeu uma saída. Porque 33 não está em um elemento dentro de um conjunto NULL/conjunto vazio.