SQL Server: Por que a comparação null = value retorna true para NOT IN?

Por que a comparação devalue paranull retorne false, exceto ao usar umNOT IN, onde retorna true?

Dada uma consulta para encontrar todos os usuários de stackoverflow queter uma postagem:

SELECT * FROM Users
WHERE UserID IN (SELECT UserID FROM Posts)

Isso funciona como esperado; eu recebo uma lista de todos os usuários que têm uma postagem.

Agora, procure o inverso; encontre todos os usuários de stackoverflow quenão tem uma postagem:

SELECT * FROM Users
WHERE UserID NOT IN (SELECT UserID FROM Posts)

Isso não retorna registros, o que está incorreto.

Dados hipotéticos dados1

Users              Posts
================   ===============================
UserID  Username   PostID   UserID  Subject
------  --------   -------  ------  ----------------
1       atkins     1        1       Welcome to stack ov...
2       joels      2        2       Welcome all!
...     ...        ...      ...
399573  gt6989b    ...      ...
...     ...        ...      ...
                   10592    null    (deleted by nsl&fbi...
                   ...      ... 

E assuma as regras dos NULLs:

NULL = NULL avalia como desconhecidoNULL <> NULL avalia como desconhecidovalue = NULL avalia desconhecido

Se observarmos a segunda consulta, estaremos interessados em encontrar todas as linhas em que o Users.UserID énão encontrado na coluna Posts.UserID. eu procederia logicamente da seguinte maneira:

Verifique UserID 1

1 = 1 retorna verdadeiro. Concluímos que este usuário tem algumas postagens e não as incluímos na lista de saída

Agora verifique o UserID 2:

2 = 1 retorna false, então continuamos procurando2 = 2 retorna true, então concluímos que esse usuário tem algumas postagens e não as incluímos na lista de saída

Agora verifique o UserID 399573

399573 = 1 retorna false, então continuamos procurando399573 = 2 retorna false, então continuamos procurando...399573 = null retorna desconhecido, por isso continuamos procurando...

Não encontramos nenhuma mensagem pelo UserID 399573, portanto o incluiríamos na lista de saída.

Exceto que o SQL Server não faz isso. Se você tiver um NULL no seuin lista e, de repente, encontra uma correspondência.De repente, encontra uma correspondência. De repente399573 = null avalia como verdadeiro.

Por que a comparação devalue paranull retornar desconhecido, exceto quando retornar true?

Editar: eu sei que posso solucionar issosem sentido comportamento, excluindo especificamente os nulos:

SELECT * FROM Users
WHERE UserID NOT IN (
   SELECT UserID FROM Posts
   WHERE UserID IS NOT NULL)

Mas eu não deveria ter que, até onde eu sei, a lógica booleana deve ficar bem sem ela - daí a minha pergunta.

$45 para46$celko agora tem sua própria tag

questionAnswers(2)

yourAnswerToTheQuestion