QA Learning / SQL

Módulo 2: NULL e filtros finos

IS NULL, COALESCE, CASE

Teoria

  • NULL = "sem valor". Não é 0 nem ''. Não dá pra comparar com =: use IS NULL / IS NOT NULL.
  • Qualquer conta com NULL vira NULL (10 + NULL = NULL).
  • COALESCE(a, b, c): devolve o primeiro argumento que não for NULL.
  • CASE WHEN condição THEN x WHEN ... THEN y ELSE z END: o "if" dentro do SELECT. Bom pra criar faixas/rótulos.
  • Truque útil: SUM(CASE WHEN cond THEN 1 ELSE 0 END) conta quantas linhas batem uma condição.

Olhar de QA: linha com data_fechamento IS NULL = defeito ainda aberto. NULL onde não devia é uma classe inteira de bug.

Exemplos de aplicação

Um exemplo montado pra cada item da teoria acima. Não precisa rodar: é pra ver a cara do recurso e entender cada pedaço. Os valores são ilustrativos.

IS NULL / IS NOT NULL

SELECT id, titulo, data_fechamento
FROM defeitos
WHERE data_fechamento IS NULL;

data_fechamento tem a data em que o defeito foi fechado, ou NULL se ainda está aberto. WHERE data_fechamento = NULL nunca dá certo (não dá pra comparar com =); o teste é IS NULL. Essa query lista os defeitos em aberto.

Conta com NULL vira NULL

SELECT id,
       data_fechamento,
       julianday(data_fechamento) - julianday(data_abertura) AS dias_aberto
FROM defeitos;

Nos defeitos fechados, dias_aberto vem com o número. Nos que têm data_fechamento nulo, a subtração inteira vira NULL, porque qualquer operação que toca em NULL resulta NULL. É por isso que linhas assim "somem" de médias e somas sem dar erro.

COALESCE(a, b, ...)

SELECT id,
       COALESCE(data_fechamento, 'ainda aberto') AS situacao
FROM defeitos;

COALESCE devolve o primeiro argumento que não for NULL. Aqui: se tem data_fechamento, mostra a data; se é NULL, mostra o texto 'ainda aberto'. Útil pra não deixar célula vazia em relatório.

CASE WHEN ... THEN ... END

SELECT nome,
       preco,
       CASE
         WHEN preco < 100 THEN 'barato'
         WHEN preco < 500 THEN 'medio'
         ELSE 'caro'
       END AS faixa_preco
FROM produtos;

É o "if" dentro do SELECT. Testa cada WHEN de cima pra baixo e para no primeiro verdadeiro; se nenhum bate, usa o ELSE. Cria a coluna faixa_preco com o rótulo. Sempre fecha com END.

SUM(CASE WHEN cond THEN 1 ELSE 0 END)

SELECT
  COUNT(*) AS total,
  SUM(CASE WHEN severidade = 'critica' THEN 1 ELSE 0 END) AS criticos,
  SUM(CASE WHEN status = 'aberto'      THEN 1 ELSE 0 END) AS em_aberto
FROM defeitos;

O CASE vira 1 quando a condição bate e 0 quando não. Somando esses valores você conta quantas linhas satisfazem cada condição, tudo numa passada. Resultado: uma linha com total, quantos são críticos e quantos estão abertos.

Pegadinhas deste módulo

Os erros que quase todo mundo comete aqui. Ler agora poupa meia hora de confusão depois, e alguns deles são a causa de relatório errado em projeto real.

  • NULL não é igual a nada, nem a outro NULL. WHERE data_fechamento = NULL devolve zero linhas sempre, mesmo havendo milhares com o campo vazio. O certo é IS NULL. Não dá erro: dá resultado vazio, que é pior.
  • <> descarta as linhas com NULL em silêncio. WHERE status <> 'fechado' não traz as linhas cujo status é NULL, porque NULL <> 'fechado' não é verdadeiro, é desconhecido. Se você quer essas linhas, precisa de OR status IS NULL. Esta é uma das maiores fontes de relatório com número errado.
  • Qualquer conta com NULL vira NULL. valor_total + frete some se o frete for nulo. Use COALESCE(frete, 0) quando o vazio deve valer zero.
  • NULL, string vazia e zero são três coisas diferentes. NULL = nunca preenchido; '' = preenchido com nada; 0 = preenchido com zero. Tratar os três como "vazio" no teste é como defeito de importação de dados passa despercebido.
  • CASE sem ELSE devolve NULL. Se nenhuma condição bater e não houver ELSE, o resultado é nulo, e a soma daquela coluna some junto.

Prática

Rode com o botão ou Ctrl+Enter.

carregando banco…

Query livre

Terminou este módulo? Marque: o ✓ aparece no índice da trilha e a barra de progresso anda no começo do site. Fica salvo só no seu navegador.