SQL MIN()
Qual é o produto mais barato da Loja Aurora? Quando foi o primeiro pedido da loja? Quem é o cliente mais velho? As três perguntas pedem o menor valor de uma coluna: o menor preço, a data mais antiga, o nascimento mais antigo. Em SQL, quem acha o menor é MIN(), de minimum, "mínimo". Ela funciona com números, com textos e com datas, e cada um tem a sua regra de "quem vem primeiro". Esta aula mostra as três, e um formato de data que faz o MIN errar sem avisar.
- Achar o menor valor de uma coluna, da tabela toda ou de um
WHERE. - O que é "menor" num texto: a ordem do SQLite, com maiúsculas e acentos.
MINcom datas: por que o formatoAAAA-MM-DDfunciona eDD/MM/AAAAnão.- Que o
MINpula oNULL, mas oORDER BYnão. MIN(preco)×ORDER BY preco LIMIT 1: o valor ou a linha inteira.- O
min()com várias colunas, que não é agregada.
A ideia: a fila por ordem de altura
Na educação física, a professora manda a turma fazer fila do mais baixo para o mais alto. O MIN é olhar quem ficou na frente. Para isso, só precisa de uma regra que diga quem vem antes: em números, o menor; em textos, a ordem alfabética; em datas, a mais antiga. É a mesma regra do ORDER BY (aula ORDER BY): MIN devolve o valor que ficaria em primeiro lugar numa ordenação crescente, deixando os NULL de fora.
1. O menor número
SELECT MIN(preco) AS mais_barato FROM produtos;
- MIN
- A função do menor valor.
- (preco)
- A coluna onde procurar. Vai uma coluna (ou uma conta);
MIN(*)não existe. - AS mais_barato
- O apelido da coluna do resultado.
O mais barato custa 1.15 (é o Lápis Grafite HB; na seção 5 você vê como mostrar o nome junto). Com WHERE, o menor de um grupo. O eletrônico mais barato:
SELECT MIN(preco) AS mais_barato FROM produtos WHERE categoria_id = 2;
Repare que o MIN devolve o valor com o tipo da coluna: preco é REAL, então 24.75 sai como está; o menor frete sai 0.0, e não 0, porque frete também é REAL:
SELECT MIN(frete) AS menor_frete FROM pedidos;
O zero é o frete grátis. Se a pergunta for "qual o frete cobrado mais barato?", tire os zeros com WHERE frete > 0: dá 7.25.
2. O menor texto
Num texto, "menor" quer dizer "vem antes na ordem". Qual nome de cliente vem primeiro, e qual cidade?
SELECT MIN(nome) AS primeiro_nome,
MIN(cidade) AS primeira_cidade
FROM clientes;
Ana Beatriz Sousa e Floriano: o primeiro de cada coluna na ordem alfabética. Só que a "ordem alfabética" do SQLite não é a do dicionário. Ele compara os textos pelo código de cada letra (a ordem chamada BINARY), e nesse código:
- todas as maiúsculas (A a Z) vêm antes de todas as minúsculas (a a z);
- as letras acentuadas (Á, É, Ó, ç…) vêm depois do z.
A Loja Aurora não tem um caso desses, então a consulta abaixo usa três nomes inventados na hora, com VALUES (uma tabelinha temporária; a coluna dela se chama column1):
SELECT MIN(column1) AS menor,
MAX(column1) AS maior
FROM (VALUES ('ana'), ('Bruno'), ('Óscar'));
O menor saiu "Bruno", e não "ana": o B maiúsculo vem antes do a minúsculo. E o maior é "Óscar", porque o Ó vem depois de todas as letras sem acento. Para ignorar maiúsculas e minúsculas, acrescente COLLATE NOCASE ("ordem sem diferenciar caixa"):
SELECT MIN(column1 COLLATE NOCASE) AS menor,
MAX(column1 COLLATE NOCASE) AS maior
FROM (VALUES ('ana'), ('Bruno'), ('Óscar'));
Agora "ana" vem primeiro. O "Óscar" continua por último: o NOCASE do SQLite só entende maiúsculas e minúsculas sem acento (de A a Z). A boa prática que evita a maior parte desses sustos é padronizar os dados ao guardar, por exemplo, nomes sempre com a primeira letra maiúscula.
3. A data mais antiga
Quando a Loja Aurora recebeu o primeiro pedido?
SELECT MIN(data) AS primeiro_pedido FROM pedidos;
No SQLite, as datas da Loja Aurora são texto no formato AAAA-MM-DD (ano, mês, dia; é o padrão internacional ISO 8601). O MIN compara esses textos letra por letra, da esquerda para a direita. Como o ano vem primeiro, depois o mês e depois o dia, a ordem do texto é exatamente a ordem do calendário. Por isso funciona também para achar o cliente mais velho, que é quem tem o menor nascimento:
SELECT MIN(nascimento) AS nasceu_primeiro FROM clientes;
30 de setembro de 1965 (é a Conceição Prado). Os dois clientes sem data de nascimento (NULL) ficaram de fora da disputa, como sempre.
Experimente: a consulta mostra o primeiro pedido de todos. A gerente quer a data do primeiro pedido de 2025. Acrescente um WHERE que deixe passar só as datas a partir de '2025-01-01'.
SELECT MIN(data) AS primeiro_pedido FROM pedidos;
SELECT MIN(data) AS primeiro_pedido FROM pedidos WHERE data >= '2025-01-01';
4. O MIN pula o NULL; o ORDER BY, não
Dois fornecedores são estrangeiros e têm estado NULL. Compare o MIN com a "primeira linha" de um ORDER BY:
SELECT MIN(estado) AS menor_estado FROM fornecedores;
SELECT estado FROM fornecedores ORDER BY estado LIMIT 1;
O MIN deu CE; o ORDER BY … LIMIT 1 deu NULL. No SQLite, o ORDER BY coloca os NULL antes de todos os outros valores, e o LIMIT 1 pegou justamente um deles. Já o MIN, como toda agregada, não deixa o NULL participar. O MIN só devolve NULL quando não há nenhum valor conhecido para comparar (por exemplo, com um WHERE que não deixa passar nenhuma linha).
5. O valor ou a linha inteira?
O MIN(preco) diz quanto custa o mais barato, mas não qual é. Para ver o nome junto, você tem três caminhos:
SELECT nome, preco FROM produtos ORDER BY preco LIMIT 1;
SELECT nome, MIN(preco) AS preco FROM produtos;
Os dois mostram o Lápis Grafite HB. O primeiro ordena tudo do mais barato para o mais caro e fica com a primeira linha. O segundo usa uma regra que só o SQLite tem: quando a consulta tem um único MIN ou MAX, as outras colunas vêm da mesma linha do valor mínimo. Em quase todos os outros bancos, essa consulta dá erro; a aula MAX() explica a regra e o erro. A terceira forma, que funciona em qualquer banco, usa uma subconsulta (aula Subconsultas):
SELECT nome, preco FROM produtos WHERE preco = (SELECT MIN(preco) FROM produtos);
| Forma | Devolve | Empates | Outros bancos |
|---|---|---|---|
MIN(preco) | Só o valor | Não importa: o valor é um só | Funciona |
ORDER BY preco LIMIT 1 | A linha inteira | Mostra só um dos empatados | Funciona (no SQL Server, com TOP 1) |
SELECT nome, MIN(preco) | A linha do mínimo | Mostra só um, sem dizer qual | Erro na maioria |
WHERE preco = (SELECT MIN(preco) …) | A linha inteira | Mostra todos os empatados | Funciona |
E cuidado com o NULL da seção 4: numa coluna com NULL, o ORDER BY … LIMIT 1 pode trazer justamente a linha do NULL. Nesse caso, filtre antes com WHERE coluna IS NOT NULL.
6. min() com várias colunas não é agregada
O SQLite tem uma segunda função min(), que recebe dois ou mais valores e devolve o menor deles, linha por linha. Ela é uma função comum, não agregada. A Ana quer limitar a exposição na vitrine a no máximo 50 unidades de cada produto:
SELECT nome, estoque, MIN(estoque, 50) AS na_vitrine FROM produtos WHERE categoria_id = 1;
Seis produtos entraram, seis linhas saíram: para cada um, o menor entre o estoque e 50. A Mochila, com 35 no estoque, fica com 35. Não confunda: MIN(estoque), com um valor, é a agregada (uma linha no resultado); MIN(estoque, 50), com dois ou mais, é a função comum.
Quando dá errado
Se as datas estivessem guardadas como DD/MM/AAAA, o MIN não daria erro nenhum, só a resposta errada. Duas datas de exemplo, 1º de dezembro de 2026 e 15 de janeiro de 2024:
SELECT MIN(column1) AS mais_antiga
FROM (VALUES ('01/12/2026'), ('15/01/2024'));
O que aconteceu: o MIN escolheu 01/12/2026, a data mais nova. Ele compara texto da esquerda para a direita: o '0' de 01 vem antes do '1' de 15, e a comparação para ali. No formato brasileiro, o dia é o que manda na ordem do texto, e não o ano.
Como consertar: guarde datas sempre no formato AAAA-MM-DD, como a Loja Aurora faz. Para mostrar no formato brasileiro, converta só na hora de exibir (aula Funções de Data).
A Ana tenta achar o produto mais barato com um WHERE:
SELECT nome FROM produtos WHERE preco = MIN(preco);
A mensagem: misuse of aggregate function MIN(), "uso indevido da função agregada MIN()". O WHERE olha uma linha por vez, e o mínimo só existe depois de ver todas.
Como consertar: a subconsulta da seção 5: WHERE preco = (SELECT MIN(preco) FROM produtos).
SELECT MIN(*) FROM produtos;
A mensagem: wrong number of arguments to function MIN(). O mesmo sai com MIN(), sem nada dentro. Diga a coluna: MIN(preco).
- Textos: a ordem depende da collation (a regra de ordenação) do banco. No MySQL 8, a padrão é
utf8mb4_0900_ai_ci, que ignora acentos (ai, accent-insensitive) e maiúsculas (ci, case-insensitive); lá, oMINdos três nomes da seção 2 não segue a ordemBINARYdo SQLite. Ao trocar de banco, confira o resultado deMIN/MAXem colunas de texto. - Várias colunas: o
min(a, b)da seção 6 é do SQLite. No MySQL e no PostgreSQL, a função que faz isso se chamaLEAST(a, b)(eGREATESTpara o maior); no SQL Server, ela existe a partir da versão 2022.
SELECT nome, estoque, LEAST(estoque, 50) AS na_vitrine FROM produtos;
Referência rápida
| Escrita | O que faz | Na Loja Aurora |
|---|---|---|
MIN(coluna) | O menor valor não NULL | MIN(preco) = 1.15 |
MIN(texto) | O primeiro na ordem BINARY (maiúsculas antes, acentos depois do z) | MIN(cidade) = Floriano |
MIN(texto COLLATE NOCASE) | Ignora maiúsculas/minúsculas (sem acento) | |
MIN(data) | A data mais antiga, se estiver em AAAA-MM-DD | MIN(data) = 2024-01-05 |
MIN(a, b, …) | Função comum: o menor entre os valores, em cada linha | MIN(estoque, 50) |
| Sem nenhuma linha | NULL |
| Mensagem do SQLite | Quer dizer |
|---|---|
misuse of aggregate function MIN() | MIN dentro do WHERE |
wrong number of arguments to function MIN() | MIN(*) ou MIN() vazio |
Exercícios
1. O menor salário
Complete para achar o menor salário da equipe:
2. A primeira entrega
Complete para achar a data da primeira entrega feita pela loja (a coluna entregue_em):
3. No SQLite, qual é o MIN dos textos 'banana', 'Uva' e 'Abacaxi'?
4. Por que MIN(data) funciona na Loja Aurora?
5. A coluna estado tem CE, PI, SP e dois NULL. O que MIN(estado) devolve?
6. Quantas linhas tem o resultado de SELECT MIN(estoque, 50) FROM produtos; (a tabela tem 45 produtos)?
Missão final: o brinquedo mais barato
Missão: a consulta lista todos os brinquedos (categoria 5). A gerente quer só uma linha: o nome e o preço do brinquedo mais barato. Use a forma que preferir da seção 5.
SELECT nome, preco FROM produtos WHERE categoria_id = 5;
SELECT nome, preco FROM produtos WHERE categoria_id = 5 ORDER BY preco LIMIT 1;
Resumo
MIN(coluna)devolve o menor valor, o que viria primeiro numORDER BYcrescente, sem contar osNULL.- Em texto, o SQLite usa a ordem
BINARY: maiúsculas antes das minúsculas, acentuadas depois do z.COLLATE NOCASEignora a caixa (sem acento). - Datas em
AAAA-MM-DDfuncionam comMIN; emDD/MM/AAAA, oMINerra sem avisar. MINpula oNULL;ORDER BY … LIMIT 1pode trazê-lo, porque no SQLite oNULLvem primeiro.- Para a linha inteira:
ORDER BY … LIMIT 1ouWHERE coluna = (SELECT MIN(coluna) …). OSELECT nome, MIN(preco)é coisa só do SQLite. MIN(a, b), com dois ou mais valores, é uma função comum, linha por linha.