Você precisa distinguir duas situações: você pode comparar uma COLUNA contra NULL ou comparar toda a ROW (RECORD) com NULL.
Considere a seguinte consulta:
SELECT
id,
txt,
txt IS NULL AS txt_is_null,
NOT txt IS NULL AS not_txt_is_null,
txt IS NOT NULL AS txt_is_not_null
FROM
(VALUES
(1::integer, NULL::text)
)
AS x(id, txt) ;
Você obtém isto:
+----+-----+-------------+-----------------+-----------------+
| id | txt | txt_is_null | not_txt_is_null | txt_is_not_null |
+----+-----+-------------+-----------------+-----------------+
| 1 | | t | f | f |
+----+-----+-------------+-----------------+-----------------+
Acho que é isso que você e eu esperávamos. Você está verificando uma COLUNA em relação a NULL e obtém "txt NÃO É NULL" e "NÃO txt É NULL" são equivalentes.
No entanto, se você fizer uma verificação diferente:
SELECT
id,
txt,
x IS NULL AS x_is_null,
NOT x IS NULL AS not_x_is_null,
x IS NOT NULL AS x_is_not_null
FROM
(VALUES
(1, NULL)
)
AS x(id, txt) ;
Então você recebe
+----+-----+-----------+---------------+---------------+
| id | txt | x_is_null | not_x_is_null | x_is_not_null |
+----+-----+-----------+---------------+---------------+
| 1 | | f | t | f |
+----+-----+-----------+---------------+---------------+
Isso pode ser surpreendente. Uma coisa parece razoável (x IS NULL) e (NOT x IS NULL) são o oposto uma da outra. A outra coisa (o fato de que nem "x É NULL" nem "x NÃO É NULL" é verdadeiro) parece estranho.
No entanto, é isso que a documentação do PostgreSQL diz que deve acontecer:
Se a expressão tiver valor de linha, IS NULL será verdadeiro quando a própria expressão de linha for nula ou quando todos os campos da linha forem nulos, enquanto IS NOT NULL for verdadeira quando a expressão de linha for não nula e todos os campos da linha forem não nulo. Devido a esse comportamento, IS NULL e IS NOT NULL nem sempre retornam resultados inversos para expressões com valor de linha; em particular, uma expressão com valor de linha que contém campos nulos e não nulos retornará false para ambos os testes. Em alguns casos, pode ser preferível escrever a linha IS DISTINCT FROM NULL ou a linha NÃO DISTINCT FROM NULL, que simplesmente verificará se o valor geral da linha é nulo sem nenhum teste adicional nos campos da linha.
Devo confessar que acho que nunca usei uma comparação com valor de linha contra nulo, mas acho que, se a possibilidade existe, pode haver algum caso de uso para ela. Eu não acho que seja comum, de qualquer maneira.