Valores Nulos

Autor

Douglas Braga

O que é NULL?

NULL é um valor especial em SQL que indica a ausência de informação. Um valor nulo pode significar:

  • Desconhecido: o valor existe, mas não é conhecido (ex.: a nota de uma disciplina ainda não lançada).
  • Inaplicável: o atributo não se aplica a essa entidade (ex.: horário de uma disciplina ainda não agendada).
  • Inexistente: a informação simplesmente não está disponível.

NULL não é zero, não é uma string vazia '' e não é false. É um valor especial que se comporta de forma diferente em operações aritméticas e comparações.

Aritmética com NULL

Qualquer operação aritmética envolvendo NULL retorna NULL:

Expressão Resultado
NULL + 60 NULL
NULL * 1.10 NULL
120 + NULL NULL

Se a nota de um aluno em uma disciplina fosse NULL (ainda não lançada), a média calculada com essa nota também seria NULL:

SELECT id_aluno, AVG(nota) AS media
FROM   matricula_disciplina
GROUP BY id_aluno;
-- Alunos com alguma nota NULL teriam resultado influenciado (NULL ignorado na média)

Verificando notas nulas — disciplinas com nota ainda não registrada:

-- Verificar se existem notas nulas no banco atual
SELECT COUNT(*) AS notas_nulas
FROM   matricula_disciplina
WHERE  nota IS NULL;
1 records
notas_nulas
0
-- Verificar se existem horários nulos em ministra (dado opcional)
SELECT COUNT(*) AS horarios_nulos
FROM   ministra
WHERE  horario IS NULL;
1 records
horarios_nulos
0

Comparações com NULL: Lógica de Três Valores

Em álgebra booleana clássica, uma comparação retorna TRUE ou FALSE. Em SQL, comparações com NULL retornam um terceiro resultado: UNKNOWN.

NULL = 5        -- UNKNOWN
NULL <> 5       -- UNKNOWN
NULL > 5        -- UNKNOWN
NULL = NULL     -- UNKNOWN (!)
Aviso

NULL = NULL retorna UNKNOWN, não TRUE. Para verificar se um valor é nulo, é obrigatório usar IS NULL ou IS NOT NULL.

Tabelas de Verdade com UNKNOWN

A lógica de três valores do SQL define como AND, OR e NOT se comportam quando um dos operandos é UNKNOWN:

AND:

A B A AND B
TRUE TRUE TRUE
TRUE UNKNOWN UNKNOWN
TRUE FALSE FALSE
UNKNOWN UNKNOWN UNKNOWN
UNKNOWN FALSE FALSE
FALSE qualquer FALSE

OR:

A B A OR B
TRUE qualquer TRUE
UNKNOWN TRUE TRUE
UNKNOWN UNKNOWN UNKNOWN
UNKNOWN FALSE UNKNOWN
FALSE FALSE FALSE

A cláusula WHERE retém apenas as linhas para as quais a condição é avaliada como TRUE. Linhas com resultado UNKNOWN são descartadas, da mesma forma que linhas com resultado FALSE.

NULL em Funções de Agregação

As funções de agregação (AVG, SUM, COUNT, MIN, MAX) ignoram valores nulos em sua computação:

Para demonstrar, inserimos uma matrícula com nota ainda não lançada para o aluno Pedro (2023311001), aproveitando a turma (3110401, 2025, 2, 1) na qual ele ainda não estava matriculado:

INSERT INTO matricula_disciplina (id_disciplina, ano, semestre, turma, id_aluno, nota, aprovado)
VALUES (3110401, 2025, 2, 1, 2023311001, NULL, NULL)
ON CONFLICT DO NOTHING;

Pedro agora tem 7 matrículas — 6 com nota lançada e 1 ainda em curso (NULL):

-- Matrículas de Pedro: 6 notas lançadas + 1 em curso (NULL)
SELECT id_disciplina, ano, semestre, nota
FROM   matricula_disciplina
WHERE  id_aluno = 2023311001
ORDER BY ano, semestre, id_disciplina;
7 records
id_disciplina ano semestre nota
3110201 2023 2 9.0
3110202 2023 2 7.4
3110401 2024 2 7.5
3110402 2024 2 6.6
3110401 2025 2 NA
3110601 2025 2 4.1
3110602 2025 2 8.7

AVG(nota) ignora a linha com NULL — o denominador é 6 (notas lançadas), não 7 (total de matrículas):

-- COUNT(*) conta tudo; COUNT(nota) e AVG ignoram NULLs
SELECT COUNT(*)                      AS total_matriculas,
       COUNT(nota)                   AS matriculas_com_nota,
       ROUND(AVG(nota)::NUMERIC, 2)  AS media_nota
FROM   matricula_disciplina
WHERE  id_aluno = 2023311001;
1 records
total_matriculas matriculas_com_nota media_nota
7 6 7.22

IS NOT NULL filtra apenas as matrículas com nota lançada:

-- Apenas disciplinas já avaliadas
SELECT id_disciplina, ano, semestre, nota
FROM   matricula_disciplina
WHERE  id_aluno = 2023311001
  AND  nota IS NOT NULL
ORDER BY ano, semestre;
6 records
id_disciplina ano semestre nota
3110201 2023 2 9.0
3110202 2023 2 7.4
3110401 2024 2 7.5
3110402 2024 2 6.6
3110601 2025 2 4.1
3110602 2025 2 8.7

IS NULL e IS NOT NULL

Para verificar a presença ou ausência de valores nulos, SQL oferece predicados especiais:

-- Registros de ministra com horário informado
SELECT id_disciplina, ano, semestre, turma, horario
FROM   ministra
WHERE  horario IS NOT NULL
LIMIT 5;
5 records
id_disciplina ano semestre turma horario
2110201 2023 2 1 2f-19:00-21:30
2110202 2023 2 1 2f-19:00-21:30
2110201 2024 2 1 3f-19:00-21:30
2110202 2024 2 1 3f-19:00-21:30
2110401 2024 2 1 2f-19:00-22:20
-- Registros de ministra sem horário
SELECT id_disciplina, ano, semestre, turma
FROM   ministra
WHERE  horario IS NULL
LIMIT 5;
0 records
id_disciplina ano semestre turma

Boas Práticas

  • Ao projetar tabelas, use NOT NULL nas colunas que sempre devem ter um valor.
  • Ao escrever condições que envolvem colunas que podem ser nulas, inclua verificações explícitas com IS NULL ou IS NOT NULL.
  • Nunca compare com = NULL; use sempre IS NULL.

Para Praticar

-- NULL em aritmética: qualquer operação com NULL resulta em NULL
SELECT id_aluno,
       nota,
       nota + 1     AS nota_mais_1,
       nota * 2     AS nota_dobro
FROM   matricula_disciplina
WHERE  nota IS NULL
LIMIT 5;
1 records
id_aluno nota nota_mais_1 nota_dobro
2023311001 NA NA NA
-- Turmas de demonstração: horário ausente, sala ausente, e ambos ausentes
INSERT INTO ministra (id_disciplina, ano, semestre, turma, id_prof, horario, sala)
VALUES
    (3110201, 2026, 2, 1, 31200001, NULL,             'ESETI-SALA-010'),
    (3110202, 2026, 2, 1, 31400002, '5f-13:45-16:15', NULL            ),
    (3110401, 2026, 2, 1, 31200001, NULL,             NULL            )
ON CONFLICT DO NOTHING;
-- COALESCE: substituir NULL por um valor padrão
SELECT id_disciplina, ano, semestre, turma,
       COALESCE(horario, 'Horário não definido') AS horario_exibido,
       COALESCE(sala,    'Sala não definida')    AS sala_exibida
FROM   ministra
ORDER BY horario NULLS FIRST, sala NULLS FIRST
LIMIT 5;
5 records
id_disciplina ano semestre turma horario_exibido sala_exibida
3110401 2026 2 1 Horário não definido Sala não definida
3110201 2026 2 1 Horário não definido ESETI-SALA-010
3110202 2023 2 1 2f-13:45-16:15/4f-13:45-16:15 ESETI-SALA-003
3110402 2024 2 1 2f-13:45-16:15/4f-13:45-16:15 ESETI-SALA-005
3110202 2024 2 1 2f-13:45-16:15/4f-13:45-16:15 ESETI-SALA-006
-- Armadilha: comparar com NULL usando = sempre retorna NULL (nunca TRUE)
-- A consulta abaixo retorna ZERO linhas, mesmo que existam notas nulas!
SELECT COUNT(*) AS resultado_incorreto
FROM   matricula_disciplina
WHERE  nota = NULL;
1 records
resultado_incorreto
0
-- Forma correta: usar IS NULL
SELECT COUNT(*) AS resultado_correto
FROM   matricula_disciplina
WHERE  nota IS NULL;
1 records
resultado_correto
1
Aviso

Armadilha clássica: WHERE coluna = NULL nunca retorna linhas — a comparação com NULL usando = sempre resulta em NULL (desconhecido), não TRUE. Use sempre IS NULL ou IS NOT NULL.

-- Remover a matrícula de demonstração inserida neste capítulo
DELETE FROM matricula_disciplina
WHERE  id_disciplina = 3110401
  AND  ano = 2025 AND semestre = 2 AND turma = 1
  AND  id_aluno = 2023311001;
-- Remover as turmas de demonstração inseridas neste capítulo
DELETE FROM ministra
WHERE  ano = 2026 AND semestre = 2 AND turma = 1
  AND  id_disciplina IN (3110201, 3110202, 3110401);