CASE WHEN: lógica condicional no SQL
1. Introdução ao CASE WHEN
A expressão CASE WHEN é a implementação padrão ANSI SQL para lógica condicional, permitindo que bancos de dados executem decisões baseadas em condições específicas durante consultas. Diferentemente de linguagens de programação tradicionais (como IF/ELSE em Python ou Java), o CASE WHEN opera diretamente no conjunto de dados, avaliando condições para cada linha retornada.
Enquanto funções como COALESCE tratam especificamente de valores nulos (retornando o primeiro valor não nulo) e NULLIF compara dois valores (retornando NULL se forem iguais), o CASE WHEN oferece flexibilidade total para definir qualquer condição booleana. Essa distinção é crucial: COALESCE e NULLIF resolvem problemas pontuais, enquanto CASE WHEN é a ferramenta genérica para lógica condicional.
2. Sintaxe Básica do CASE WHEN
Existem duas formas principais de CASE WHEN:
CASE Simples — compara uma expressão com valores fixos:
SELECT
nome,
CASE status
WHEN 'A' THEN 'Ativo'
WHEN 'I' THEN 'Inativo'
WHEN 'P' THEN 'Pendente'
ELSE 'Desconhecido'
END AS status_descricao
FROM clientes;
CASE Pesquisado — avalia condições booleanas arbitrárias:
SELECT
produto,
preco,
CASE
WHEN preco < 50 THEN 'Barato'
WHEN preco BETWEEN 50 AND 200 THEN 'Médio'
WHEN preco > 200 THEN 'Caro'
ELSE 'Sem preço'
END AS categoria_preco
FROM produtos;
A ordem de avaliação é sequencial: a primeira condição verdadeira define o resultado. O ELSE é opcional, mas sua ausência retorna NULL quando nenhuma condição é satisfeita.
3. Usando CASE WHEN em SELECT
Categorizando valores de uma coluna:
SELECT
aluno,
nota,
CASE
WHEN nota >= 90 THEN 'Excelente'
WHEN nota >= 70 THEN 'Bom'
WHEN nota >= 50 THEN 'Regular'
ELSE 'Insuficiente'
END AS classificacao
FROM notas;
Colunas calculadas condicionais:
SELECT
nome,
salario,
CASE
WHEN cargo = 'Gerente' THEN salario * 1.20
WHEN cargo = 'Analista' THEN salario * 1.10
ELSE salario * 1.05
END AS salario_reajustado
FROM funcionarios;
Combinando com funções de agregação:
SELECT
departamento,
COUNT(*) AS total_funcionarios,
SUM(CASE WHEN salario > 5000 THEN 1 ELSE 0 END) AS funcionarios_acima_5k,
AVG(CASE WHEN YEAR(data_admissao) = 2023 THEN salario ELSE NULL END) AS media_salario_novatos
FROM funcionarios
GROUP BY departamento;
4. CASE WHEN em Cláusulas WHERE e ORDER BY
Filtrando com condições dinâmicas no WHERE:
SELECT
pedido_id,
data_pedido,
valor_total
FROM pedidos
WHERE
CASE
WHEN EXTRACT(YEAR FROM data_pedido) = 2023 THEN valor_total > 1000
WHEN EXTRACT(YEAR FROM data_pedido) = 2024 THEN valor_total > 500
ELSE valor_total > 200
END;
Ordenação personalizada com CASE no ORDER BY:
SELECT
tarefa,
prioridade,
data_vencimento
FROM tarefas
ORDER BY
CASE prioridade
WHEN 'Urgente' THEN 1
WHEN 'Alta' THEN 2
WHEN 'Média' THEN 3
WHEN 'Baixa' THEN 4
ELSE 5
END,
data_vencimento ASC;
Este exemplo ordena tarefas por prioridade personalizada, depois por data de vencimento ascendente.
5. CASE WHEN em Atualizações e Inserções (UPDATE / INSERT)
Atualizando valores condicionalmente:
UPDATE produtos
SET preco = CASE
WHEN categoria = 'Eletrônicos' THEN preco * 0.90
WHEN categoria = 'Roupas' THEN preco * 0.80
WHEN estoque < 10 THEN preco * 0.95
ELSE preco
END;
Inserindo dados derivados:
INSERT INTO historico_precos (produto_id, preco_antigo, preco_novo, data_alteracao)
SELECT
id,
preco,
CASE
WHEN promocao = 'S' THEN preco * 0.85
ELSE preco
END,
CURRENT_DATE
FROM produtos;
Cuidados com performance: Em operações em lote com milhões de registros, CASE WHEN pode ser mais eficiente que múltiplas consultas UPDATE separadas, mas sempre avalie o plano de execução.
6. CASE WHEN Aninhado e Combinações Avançadas
CASE WHEN aninhado para lógica complexa:
SELECT
cliente_id,
CASE
WHEN total_compras > 10000 THEN
CASE
WHEN frequencia_compras > 20 THEN 'VIP Premium'
ELSE 'VIP'
END
WHEN total_compras > 5000 THEN 'Ouro'
WHEN total_compras > 1000 THEN 'Prata'
ELSE 'Bronze'
END AS nivel_fidelidade
FROM clientes;
Combinando com subconsultas e JOINs:
SELECT
p.nome,
p.preco,
CASE
WHEN p.preco > (SELECT AVG(preco) FROM produtos) THEN 'Acima da média'
ELSE 'Abaixo ou igual à média'
END AS comparacao_media
FROM produtos p
LEFT JOIN categorias c ON p.categoria_id = c.id;
Pivotando dados (transformar linhas em colunas):
SELECT
departamento,
SUM(CASE WHEN mes = 'Janeiro' THEN vendas ELSE 0 END) AS jan,
SUM(CASE WHEN mes = 'Fevereiro' THEN vendas ELSE 0 END) AS fev,
SUM(CASE WHEN mes = 'Março' THEN vendas ELSE 0 END) AS mar
FROM vendas_mensais
GROUP BY departamento;
7. Boas Práticas e Armadilhas Comuns
Evitando ambiguidade com ELSE e tratamento de nulos:
-- PROBLEMA: NULL não é capturado por nenhum WHEN
SELECT
CASE
WHEN valor > 0 THEN 'Positivo'
WHEN valor < 0 THEN 'Negativo'
-- ELSE implícito retorna NULL para valor = 0 ou NULL
END
-- SOLUÇÃO: ELSE explícito e tratamento de NULL
SELECT
CASE
WHEN valor > 0 THEN 'Positivo'
WHEN valor < 0 THEN 'Negativo'
WHEN valor = 0 THEN 'Zero'
ELSE 'Sem valor'
END
Performance: CASE WHEN geralmente é eficiente, mas para substituições simples de valores, funções específicas do banco (como DECODE no Oracle ou IIF no SQL Server) podem ser mais rápidas. Teste com seu SGBD.
Legibilidade: Para lógica simples de substituição de nulos, prefira COALESCE:
-- Menos legível
SELECT CASE WHEN nome IS NULL THEN 'Sem nome' ELSE nome END
-- Mais legível
SELECT COALESCE(nome, 'Sem nome')
8. Exemplos Práticos no PostgreSQL
Categorizar vendas por trimestre com CASE + funções de data:
SELECT
EXTRACT(YEAR FROM data_venda) AS ano,
CASE
WHEN EXTRACT(MONTH FROM data_venda) BETWEEN 1 AND 3 THEN 'Q1'
WHEN EXTRACT(MONTH FROM data_venda) BETWEEN 4 AND 6 THEN 'Q2'
WHEN EXTRACT(MONTH FROM data_venda) BETWEEN 7 AND 9 THEN 'Q3'
ELSE 'Q4'
END AS trimestre,
SUM(valor) AS total_vendas,
COUNT(*) AS quantidade_vendas
FROM vendas
GROUP BY ano, trimestre
ORDER BY ano, trimestre;
Classificar usuários por nível de atividade com NULLIF:
SELECT
usuario_id,
CASE
WHEN NULLIF(ultimo_acesso, primeiro_acesso) IS NULL THEN 'Nunca retornou'
WHEN ultimo_acesso > CURRENT_DATE - INTERVAL '30 days' THEN 'Ativo'
WHEN ultimo_acesso > CURRENT_DATE - INTERVAL '90 days' THEN 'Inativo'
ELSE 'Abandonou'
END AS status_atividade
FROM usuarios;
Relatório de desempenho com CASE e GROUP BY:
SELECT
vendedor_id,
COUNT(*) AS total_vendas,
SUM(CASE WHEN valor > 1000 THEN 1 ELSE 0 END) AS vendas_acima_1k,
ROUND(
100.0 * SUM(CASE WHEN valor > 1000 THEN 1 ELSE 0 END) / COUNT(*),
2
) AS percentual_vendas_alto_valor,
CASE
WHEN SUM(CASE WHEN valor > 1000 THEN 1 ELSE 0 END) > 50 THEN 'Performance Excelente'
WHEN SUM(CASE WHEN valor > 1000 THEN 1 ELSE 0 END) > 20 THEN 'Performance Boa'
ELSE 'Performance Regular'
END AS avaliacao
FROM vendas
WHERE data_venda >= '2024-01-01'
GROUP BY vendedor_id
HAVING COUNT(*) > 10
ORDER BY percentual_vendas_alto_valor DESC;
Referências
- Documentação Oficial PostgreSQL: Expressões Condicionais — Documentação completa sobre CASE, COALESCE, NULLIF e outras expressões condicionais no PostgreSQL
- W3Schools SQL CASE Statement — Tutorial introdutório com exemplos práticos da sintaxe CASE WHEN em SQL
- SQL Server CASE Expression (Microsoft Learn) — Documentação oficial da Microsoft sobre CASE expression no SQL Server, com exemplos e boas práticas
- Mode Analytics SQL Tutorial: CASE WHEN — Tutorial interativo com exemplos reais de CASE WHEN em consultas analíticas
- SQLShack: CASE Statement in SQL — Artigo técnico detalhado sobre uso avançado de CASE WHEN, incluindo performance e armadilhas comuns