SQL CASE

No estoque da Loja Aurora, a Ana cola uma etiqueta em cada prateleira: se o produto acabou, etiqueta vermelha "esgotado"; se está acabando, amarela "repor"; senão, verde "ok". Essa decisão de "se isso, então aquilo" também cabe numa consulta. Quem faz isso em SQL é o CASE ("caso"): ele examina cada linha e devolve um valor diferente conforme a situação.

Você vai aprender
  • O CASE buscado: CASE WHEN condição THEN valor … ELSE … END.
  • Por que a ordem dos WHEN importa, e o que acontece sem ELSE.
  • O CASE simples: CASE coluna WHEN valor THEN …, e quando ele não serve.
  • O CASE no ORDER BY, para uma ordem que não é alfabética nem numérica.
  • O CASE com SUM e COUNT, para contar só o que interessa.
  • O atalho iif() e os erros reais do SQLite.

Todos os exemplos rodam de verdade no seu navegador, no banco da Loja Aurora (veja O Banco da Loja Aurora).


A ideia: as etiquetas da prateleira

A regra da Ana, escrita em português, é uma lista de perguntas feitas em ordem, parando na primeira resposta "sim":

Caso o estoque seja 0           → "esgotado"
senão, caso seja menor que 20   → "repor"
senão                           → "ok"

Em SQL, a mesma regra fica quase igual, só que em inglês: CASE = "caso", WHEN = "quando", THEN = "então", ELSE = "senão", END = "fim".


1. O primeiro CASE: faixas de preço

Comece com uma regra de três faixas para os eletrônicos: abaixo de 20 é "barato", abaixo de 100 é "médio", o resto é "caro".

SELECT nome,
       preco,
       CASE
         WHEN preco < 20  THEN 'barato'
         WHEN preco < 100 THEN 'médio'
         ELSE 'caro'
       END AS faixa
FROM produtos
WHERE categoria_id = 2;
CASE
Abre a decisão. Tudo até o END é uma coluna só do resultado.
WHEN preco < 20 THEN 'barato'
"Quando o preço for menor que 20, então o valor é 'barato'." Depois do WHEN vem qualquer condição, igual às do WHERE.
WHEN preco < 100 THEN 'médio'
Só é examinado se o WHEN de cima deu "não". Por isso não precisa escrever preco >= 20 AND preco < 100: quem chegou aqui já não é menor que 20.
ELSE 'caro'
"Senão": o valor para quem não se encaixou em nenhum WHEN.
END AS faixa
Fecha o CASE. O AS faixa dá nome à coluna; sem ele, o cabeçalho seria o texto inteiro do CASE.

Repare que a tabela produtos não tem coluna faixa: ela foi calculada na hora, linha por linha. O banco não muda nada; só a resposta ganha uma coluna.

Este jeito, com uma condição depois de cada WHEN, se chama CASE buscado (searched CASE): o banco "busca" a primeira condição verdadeira.


2. A ordem dos WHEN importa

O CASE para no primeiro WHEN verdadeiro e ignora os seguintes. Veja o que acontece se a Ana inverter as duas primeiras linhas, na Papelaria:

SELECT nome,
       preco,
       CASE
         WHEN preco < 100 THEN 'médio'
         WHEN preco < 20  THEN 'barato'
         ELSE 'caro'
       END AS faixa
FROM produtos
WHERE categoria_id = 1;

Tudo virou "médio", até o lápis de 1.15. Ele é menor que 100, então o primeiro WHEN já respondeu, e o WHEN preco < 20 nunca chega a ser examinado. Com a ordem certa (a mais restrita primeiro):

SELECT nome,
       preco,
       CASE
         WHEN preco < 20  THEN 'barato'
         WHEN preco < 100 THEN 'médio'
         ELSE 'caro'
       END AS faixa
FROM produtos
WHERE categoria_id = 1;
Como acertar a ordem

Com faixas de "menor que", vá do menor limite para o maior (20, depois 100). Com "maior que", do maior para o menor. Em qualquer caso, leia o CASE de cima para baixo e pergunte: "algum WHEN de cima já pega este caso?".


3. Sem ELSE, vem NULL

O ELSE é opcional. Mas, sem ele, a linha que não se encaixa em nenhum WHEN recebe NULL (aula O Valor NULL):

SELECT nome,
       preco,
       CASE WHEN preco >= 100 THEN 'caro' END AS faixa
FROM produtos
WHERE categoria_id = 2;

Só os dois eletrônicos de 100 para cima ganharam "caro"; os outros, NULL. Às vezes é isso que você quer (a seção 6 usa esse comportamento de propósito). Quando não for, escreva o ELSE.

Missão: a coluna vip guarda 1 ou 0, e a Ana quer ler "VIP" ou "comum". Troque a coluna vip por um CASE com o nome tipo: quando vip = 1, 'VIP'; senão, 'comum'.

SELECT nome, vip
FROM clientes
WHERE cidade = 'Teresina';
SELECT nome,
       CASE WHEN vip = 1 THEN 'VIP' ELSE 'comum' END AS tipo
FROM clientes
WHERE cidade = 'Teresina';

4. O CASE simples

Quando todas as perguntas são "a coluna é igual a tal valor?", existe uma escrita mais curta: o nome da coluna vem logo depois do CASE, e cada WHEN traz só o valor.

SELECT nome,
       estoque,
       CASE descontinuado
         WHEN 0 THEN 'à venda'
         WHEN 1 THEN 'fora de linha'
       END AS situacao
FROM produtos
WHERE categoria_id = 5;
CASE descontinuado
A coluna (ou expressão) que vai ser comparada. Ela aparece uma vez só.
WHEN 0 THEN 'à venda'
"Quando descontinuado for igual a 0, então…". O = fica implícito.

Isso se chama CASE simples. Ele é o mesmo que escrever CASE WHEN descontinuado = 0 THEN … WHEN descontinuado = 1 THEN … END, só que mais curto. O Carrinho de Fricção é um dos dois produtos descontinuados da loja.

Simples × buscado: qual usar

CASE simplesCASE buscado
EscritaCASE coluna WHEN valor THEN …CASE WHEN condição THEN …
ComparaSó igualdade (=) com uma colunaQualquer condição: <, >, LIKE, IN, IS NULL, AND, OR, colunas diferentes…
Bom paraTraduzir códigos: 0/1, siglas, tiposFaixas, regras com várias colunas, NULL
Funciona com NULL?Não (veja Quando dá errado)Sim, com WHEN coluna IS NULL

Na dúvida, use o buscado: ele faz tudo o que o simples faz, e mais.


5. CASE no ORDER BY: uma ordem sob medida

O CASE pode ir em qualquer lugar onde caberia um valor, inclusive no ORDER BY. A Ana quer a lista de funcionários na ordem da hierarquia: gerente, supervisora, vendedores, estoquista. Essa ordem não é alfabética (Estoquista viria antes de Gerente). A saída é dar um número a cada cargo e ordenar pelo número:

SELECT nome, cargo
FROM funcionarios
ORDER BY CASE cargo
           WHEN 'Gerente'     THEN 1
           WHEN 'Supervisora' THEN 2
           WHEN 'Estoquista'  THEN 4
           ELSE 3
         END,
         nome;

O CASE calcula um número para cada linha, e o ORDER BY ordena por ele, sem mostrá-lo. Os vendedores e vendedoras caíram no ELSE 3, e o segundo critério, nome, põe os cinco em ordem alfabética.

Outro uso comum: "os pendentes primeiro". Os pedidos de agosto de 2026 para cá, com os não entregues no topo:

SELECT id, data, entregue_em
FROM pedidos
WHERE data >= '2026-08-01'
ORDER BY CASE WHEN entregue_em IS NULL THEN 0 ELSE 1 END,
         data;

6. CASE com SUM e COUNT: contar por condição

Esta é uma das combinações mais úteis do SQL. A Ana quer, para cada atendente, o total de pedidos, quantos tiveram frete grátis e quantos ainda estão pendentes, tudo numa linha só por pessoa. Com WHERE, ela precisaria de três consultas. Com CASE dentro do SUM:

SELECT f.nome AS atendente,
       COUNT(*) AS pedidos,
       SUM(CASE WHEN pe.frete = 0 THEN 1 ELSE 0 END) AS frete_gratis,
       SUM(CASE WHEN pe.entregue_em IS NULL THEN 1 ELSE 0 END) AS pendentes
FROM pedidos AS pe
INNER JOIN funcionarios AS f ON f.id = pe.funcionario_id
GROUP BY f.id, f.nome;
CASE WHEN pe.frete = 0 THEN 1 ELSE 0 END
Para cada pedido, vale 1 se o frete foi grátis e 0 se não foi.
SUM( … )
Somando esses 1 e 0, sobra a quantidade de pedidos com frete grátis. É como passar o dedo na lista e fazer um risquinho só nos que interessam.

Cada atendente tem 15 pedidos; a Luana e o Igor deram 4 fretes grátis cada um; só o Caio não tem pedido pendente. O mesmo truque funciona com COUNT, aproveitando o comportamento da seção 3: sem ELSE, o CASE devolve NULL, e o COUNT(coluna) não conta NULL:

SELECT COUNT(CASE WHEN vip = 1 THEN 1 END) AS vips,
       COUNT(*) AS total
FROM clientes;

Cinco VIPs em 30 clientes. Também dá para agrupar por um CASE: quantos produtos há em cada situação de estoque.

SELECT CASE
         WHEN estoque = 0  THEN 'esgotado'
         WHEN estoque < 20 THEN 'repor'
         ELSE 'ok'
       END AS situacao,
       COUNT(*) AS produtos
FROM produtos
GROUP BY situacao;

Missão: numa linha só, mostre o total de clientes e quantos são do Piauí e do Ceará. A coluna piaui já está pronta; acrescente a coluna ceara do mesmo jeito (estado = 'CE').

SELECT COUNT(*) AS clientes,
       SUM(CASE WHEN estado = 'PI' THEN 1 ELSE 0 END) AS piaui
FROM clientes;
SELECT COUNT(*) AS clientes,
       SUM(CASE WHEN estado = 'PI' THEN 1 ELSE 0 END) AS piaui,
       SUM(CASE WHEN estado = 'CE' THEN 1 ELSE 0 END) AS ceara
FROM clientes;

7. Formas diferentes: iif() e CASE com EXISTS

Para a decisão mais comum, "se isso, um valor; senão, outro", o SQLite tem um atalho, a função iif(). A documentação do SQLite diz que iif(X, Y, Z) é equivalente a CASE WHEN X THEN Y ELSE Z END e gera o mesmo código por dentro. Ela também aceita o nome if().

SELECT nome, iif(vip = 1, 'VIP', 'comum') AS tipo
FROM clientes
WHERE cidade = 'Recife' OR vip = 1;

O CASE também combina com o EXISTS, para trocar o 0/1 por palavras:

SELECT c.nome,
       CASE WHEN EXISTS (SELECT 1 FROM pedidos AS pe WHERE pe.cliente_id = c.id)
            THEN 'sim' ELSE 'não'
       END AS ja_comprou
FROM clientes AS c
WHERE c.cidade = 'Fortaleza';
FormaQuando usar
CASE WHEN … THEN … ELSE … ENDSempre funciona, em qualquer banco. A escolha padrão.
CASE coluna WHEN valor THEN … ENDTraduzir códigos de uma coluna (sem NULL)
iif(condição, sim, não)Atalho para dois resultados; não existe em todo banco (veja a nota abaixo)
IFNULL / COALESCESó para trocar NULL por outro valor: aula IFNULL e COALESCE

Quando dá errado

Esqueceu o END
SELECT nome,
       CASE WHEN preco < 20 THEN 'barato' ELSE 'caro' AS faixa
FROM produtos;

A mensagem: near "AS": syntax error, "erro de sintaxe perto de AS". O banco ainda estava dentro do CASE esperando o END quando encontrou o AS. Sem o AS, o erro aparece mais adiante: near "FROM": syntax error.

Como consertar: todo CASE termina em END: … ELSE 'caro' END AS faixa.

Esqueceu o THEN
SELECT nome,
       CASE WHEN preco < 20 'barato' ELSE 'caro' END AS faixa
FROM produtos;

A mensagem: near "'barato'": syntax error. Depois da condição, o banco esperava o THEN.

Como consertar: WHEN preco < 20 THEN 'barato'.

WHEN NULL no CASE simples: nunca acontece

A Ana quis escrever "sem email" para quem não tem email, com um CASE simples:

SELECT nome,
       CASE email WHEN NULL THEN 'sem email' ELSE email END AS contato
FROM clientes
WHERE id IN (6, 7, 15);

O que acontece: nenhum erro, mas o Luís e o Roberto, que não têm email, continuam com NULL. O CASE simples compara com =, e email = NULL nunca é verdadeiro, nem quando o email é NULL (aula IS NULL). Esse WHEN nunca dispara.

Como consertar: use o CASE buscado com IS NULL:

SELECT nome,
       CASE WHEN email IS NULL THEN 'sem email' ELSE email END AS contato
FROM clientes
WHERE id IN (6, 7, 15);

(Para esse caso específico, trocar NULL por um texto, existe um atalho ainda mais curto: IFNULL e COALESCE.)

OR dentro do WHEN do CASE simples
SELECT nome,
       CASE cargo WHEN 'Vendedor' OR 'Vendedora' THEN 'vendas' ELSE 'outro' END AS setor
FROM funcionarios;

O que acontece: todo mundo virou "outro", até os cinco vendedores. O banco calculou primeiro 'Vendedor' OR 'Vendedora', que vale 0 (falso: um texto que não parece número conta como 0), e depois comparou cargo = 0. Nenhum cargo é 0.

Como consertar: use o CASE buscado com IN (aula IN): CASE WHEN cargo IN ('Vendedor', 'Vendedora') THEN 'vendas' ELSE 'outro' END. No CASE simples, cada valor vai num WHEN próprio.

Resultados de tipos diferentes
SELECT nome,
       CASE WHEN vip = 1 THEN 'VIP' ELSE 0 END AS tipo
FROM clientes
WHERE cidade = 'Recife' OR vip = 1;

O que acontece: o SQLite aceita uma coluna com texto ('VIP') em umas linhas e número (0) em outras. Funciona, mas confunde quem lê, e os outros bancos podem recusar ou converter os valores.

Como consertar: faça todos os THEN e o ELSE devolverem o mesmo tipo: ELSE 'comum'.

Nos outros bancos
  • O CASE, nas duas formas, é SQL padrão e se escreve igual no MySQL, no PostgreSQL e no SQL Server; sem ELSE, também devolve NULL.
  • O atalho muda de nome: no MySQL é IF(condição, sim, não); no SQL Server, IIF(condição, sim, não), que o manual da Microsoft chama de "um jeito abreviado de escrever um CASE". Para funcionar em qualquer lugar, prefira o CASE.
  • Apelido no WHERE: o SQLite aceita usar o nome dado pelo AS (por exemplo, WHERE faixa = 'caro'), mas o manual do MySQL lembra que o SQL padrão não permite apelidos de coluna no WHERE. Para um código que funcione em todo banco, repita o CASE no WHERE ou use uma subconsulta no FROM.

Referência rápida

EscritaO que faz
CASE WHEN c1 THEN v1 WHEN c2 THEN v2 ELSE v3 ENDCASE buscado: o valor do primeiro WHEN verdadeiro
CASE col WHEN a THEN v1 WHEN b THEN v2 ELSE v3 ENDCASE simples: compara col = a, col = b…
Sem ELSEQuem não se encaixa recebe NULL
… END AS nomeDá nome à coluna calculada
ORDER BY CASE … ENDOrdem sob medida
SUM(CASE WHEN c THEN 1 ELSE 0 END)Conta as linhas em que c é verdadeira
COUNT(CASE WHEN c THEN 1 END)O mesmo, usando o NULL do CASE sem ELSE
iif(c, sim, não)Atalho do SQLite para CASE WHEN c THEN sim ELSE não END
Mensagem ou sintomaQuer dizer
near "AS": syntax error / near "FROM": syntax errorFaltou o END
near "'barato'": syntax errorFaltou o THEN
Tudo caiu no mesmo WHENA ordem dos WHEN está errada (o mais amplo veio primeiro)
WHEN NULL nunca disparaCASE simples compara com =; use WHEN col IS NULL

Exercícios

1. Feche o CASE

Complete a palavra que falta:

CASE WHEN estoque = 0 THEN 'esgotado' ELSE 'ok' AS situacao

2. O CASE simples

Complete o CASE simples que traduz o vip:

CASE vip 1 THEN 'VIP' ELSE 'comum' END

3. Com CASE WHEN preco < 100 THEN 'médio' WHEN preco < 20 THEN 'barato' ELSE 'caro' END, qual é a faixa do Lápis (1.15)?

4. O que um CASE sem ELSE devolve para a linha que não se encaixa em nenhum WHEN?

5. Por que CASE email WHEN NULL THEN 'sem email' END não funciona?

6. Para contar, numa linha só, quantos pedidos tiveram frete grátis, a Ana pode usar:

Missão final: as etiquetas do estoque

Missão: faça as etiquetas da Ana para os 45 produtos. Acrescente a coluna situacao: estoque 0 é 'esgotado'; menor que 20 é 'repor'; o resto é 'ok'. Cuidado com a ordem dos WHEN. Se quiser, ordene para os esgotados aparecerem primeiro.

SELECT nome, estoque
FROM produtos;
SELECT nome,
       estoque,
       CASE
         WHEN estoque = 0  THEN 'esgotado'
         WHEN estoque < 20 THEN 'repor'
         ELSE 'ok'
       END AS situacao
FROM produtos
ORDER BY estoque;

Resumo

  • CASE é o "se isso, então aquilo" do SQL: ele calcula um valor para cada linha. Termina sempre em END; dê um nome com AS.
  • CASE buscado (CASE WHEN condição THEN …): qualquer condição. CASE simples (CASE coluna WHEN valor THEN …): só igualdade, e não serve para NULL.
  • O CASE para no primeiro WHEN verdadeiro: a ordem importa. Sem ELSE, quem sobra recebe NULL.
  • No ORDER BY, o CASE cria uma ordem sob medida.
  • Com SUM(CASE WHEN … THEN 1 ELSE 0 END), você conta por condição, várias contagens numa linha só.
  • iif() é um atalho do SQLite (no MySQL, IF(); no SQL Server, IIF()); o CASE funciona em todos.