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.

Você vai aprender
  • 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.
  • MIN com datas: por que o formato AAAA-MM-DD funciona e DD/MM/AAAA não.
  • Que o MIN pula o NULL, mas o ORDER BY nã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);
FormaDevolveEmpatesOutros bancos
MIN(preco)Só o valorNão importa: o valor é um sóFunciona
ORDER BY preco LIMIT 1A linha inteiraMostra só um dos empatadosFunciona (no SQL Server, com TOP 1)
SELECT nome, MIN(preco)A linha do mínimoMostra só um, sem dizer qualErro na maioria
WHERE preco = (SELECT MIN(preco) …)A linha inteiraMostra todos os empatadosFunciona

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

Datas no formato brasileiro

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).

MIN no WHERE

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).

MIN(*) ou MIN vazio
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).

Nos outros bancos
  • 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á, o MIN dos três nomes da seção 2 não segue a ordem BINARY do SQLite. Ao trocar de banco, confira o resultado de MIN/MAX em 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 chama LEAST(a, b) (e GREATEST para 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

EscritaO que fazNa Loja Aurora
MIN(coluna)O menor valor não NULLMIN(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-DDMIN(data) = 2024-01-05
MIN(a, b, …)Função comum: o menor entre os valores, em cada linhaMIN(estoque, 50)
Sem nenhuma linhaNULL
Mensagem do SQLiteQuer 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:

SELECT (salario) FROM funcionarios;

2. A primeira entrega

Complete para achar a data da primeira entrega feita pela loja (a coluna entregue_em):

SELECT MIN() FROM pedidos;

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 num ORDER BY crescente, sem contar os NULL.
  • Em texto, o SQLite usa a ordem BINARY: maiúsculas antes das minúsculas, acentuadas depois do z. COLLATE NOCASE ignora a caixa (sem acento).
  • Datas em AAAA-MM-DD funcionam com MIN; em DD/MM/AAAA, o MIN erra sem avisar.
  • MIN pula o NULL; ORDER BY … LIMIT 1 pode trazê-lo, porque no SQLite o NULL vem primeiro.
  • Para a linha inteira: ORDER BY … LIMIT 1 ou WHERE coluna = (SELECT MIN(coluna) …). O SELECT nome, MIN(preco) é coisa só do SQLite.
  • MIN(a, b), com dois ou mais valores, é uma função comum, linha por linha.