Esse artigo foi extraído do meu livro Firebird 2.0, e revisado em 26-04-2022 - Carlos H. Cantu
O NULL sempre foi encarado com certo mistério por grande parte dos desenvolvedores. Na maioria das vezes, isso se deve às pessoas não atentarem ao fato do NULL não ser um valor, mas sim um estado indefinido. Se um argumento ou campo tem um valor definido, então ele não é nulo.
Alguns bancos de dados “desktop” tratam o null como equivalente ao zero (0) em campos numéricos, ou como strings vazios (‘’) em campos alfa-numéricos. O Firebird não age assim, e você deve estar preparado para entender o comportamento do NULL em diversas situações, se não quiser ter surpresas desagradáveis.
Como null não é um valor e sim um estado, o Firebird possui um operador específico para saber se um argumento é nulo ou não. Esse operador é o IS NULL (É NULO) ou sua negação, IS NOT NULL (NÃO É NULO).
Fique atento às expressões matemáticas envolvendo variáveis que podem ser nulas. Quando qualquer uma das variáveis de uma fórmula matemática for nula, o resultado também será nulo. Isso também vale para operações de concatenação de strings.
A tabela abaixo mostra alguns exemplos de comparação envolvendo um argumento nulo, bem como o resultado obtido e sua justificativa.
Comando |
Resultado |
Justificativa |
SELECT CASE
WHEN (1 = NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
FALSO |
Qualquer comparação ( =, <>, >, <, etc.) onde um dos argumentos é nulo, sempre retornará falso, pois NULL é um estado e não um valor. |
SELECT CASE
WHEN (NULL <> 1) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
FALSO |
SELECT CASE
WHEN (NULL = NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
FALSO |
SELECT CASE
WHEN (NULL is NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
VERDADE |
Nulo é nulo, portanto a condição é verdadeira. |
SELECT CASE
WHEN (1 is not NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
VERDADE |
O número um é um valor definido, portanto a expressão é verdadeira. |
SELECT CASE
WHEN (null is not NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
FALSO |
Já vimos que nulo é nulo, portanto dizer que nulo “não é” nulo é uma afirmação falsa. |
SELECT CASE
WHEN ('' is NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
VERDADE |
Uma string em branco (‘’) é uma string em branco, portanto não é nulo. |
SELECT CASE
WHEN ((1 + NULL)is NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
VERDADE |
Qualquer fórmula matemática onde uma das variáveis seja nula fará com que o resultado também seja nulo. |
SELECT CASE
WHEN (('ABC' || NULL) is NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database |
VERDADE |
A junção de uma string com NULL sempre resultará em NULL. Note que o resultado não é uma string vazia, e sim NULO. |
Até o Firebird 1.5, o uso de operadores de comparação onde um dos argumentos é nulo resultará em erro. A partir do Firebird 2.0 essa regra foi relaxada e, ao invés de gerar um erro, o resultado será sempre false.
arg1 IS [NOT] DISTINCT FROM arg2
O Firebird 2.0 trouxe um novo predicado de equivalência: o DISTINCT. Vimos anteriormente que uma comparação do tipo NULL = NULL retorna falso, pois null é um estado desconhecido, e não um valor.
Se usarmos o NOT DISTINCT, o resultado será verdadeiro, pois o distinct verifica também se os argumentos (arg1 e arg2) são distintos um do outro. Vejamos alguns exemplos:
SELECT CASE
WHEN (NULL IS DISTINCT FROM NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database
Retorno: FALSO
SELECT CASE
WHEN (1 IS DISTINCT FROM NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database
Retorno: VERDADE
SELECT CASE
WHEN (1 IS DISTINCT FROM 1) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database
Retorno: FALSO
SELECT CASE
WHEN (NULL IS NOT DISTINCT FROM NULL) THEN 'VERDADE'
ELSE 'FALSO' END
FROM rdb$database
Retorno: VERDADE
Imagine, como exemplo, que desejamos retornar todos os registros da tabela de clientes onde a cidade seja diferente de SAPULPA. Geralmente o primeiro comando que nos vem à mente é:
select *
from clientes
where cidade <> ‘SAPULPA’
No entanto, o comando acima não trará os registros cujo campo cidade estiver nulo, ou seja, sem cidade definida. Para trazer os registros com cidade diferente de SAPULPA, incluindo aqueles cuja cidade não foi especificada, devemos usar o distinct:
select *
from clientes
where cidade is distinct from ‘SAPULPA’
Tenha cuidado em stored procedures e triggers, quando estiver trabalhando com comandos condicionais (ex: IF, WHILE) onde qualquer um dos argumentos pode assumir nulo. Lembre-se sempre de checar essa situação através do uso do DISTINCT ou do IS NULL, quando necessário, por exemplo:
/* Busca o maior valor do campo num de uma tabela para os registros com ID = 1000 */
select max(num) from tabela where ID = 1000 into :variavel1;
/* Se não há registros com valores para o ID 1000, então desejaremos inserir um novo registro. É necessário checar também se variavel1 é nula, pois no caso de não haver nenhum registro cadastrado com ID 1000, ela estará nula */
If ((:variavel1 = 0) or (:variavel1 is null)) then
Insert into tabela (id, num) values (1000, 1);
Conclusão
O correto entendimento do comportamento do NULL nas diversas situações é muito importante para a definição correta da lógica envolvida em operações condicionais. Procure sempre testar todas as possibilidades, valores e estados das variáveis envolvidas, a fim de tratar todas as variações.