SQL MAX()
Qual é o produto mais caro da Loja Aurora? Quando foi o último pedido? Qual o maior salário? MAX(), de maximum, "máximo", é a irmã da MIN(): mesma regra, ponta oposta. Ela devolve o valor que ficaria em último lugar numa ordenação crescente. Esta aula mostra o MAX com números, textos e datas e depois trata de uma consulta que todo iniciante escreve, SELECT nome, MAX(preco). No SQLite ela funciona, e na maioria dos outros bancos ela dá erro.
- Achar o maior valor de uma coluna, de uma conta ou de um grupo com
WHERE. MAXcom textos e datas, e o susto do número guardado como texto.- O que é uma coluna nua (
SELECT nome, MAX(preco)) e a regra especial do SQLite para ela. - Quando a coluna nua engana: empates, duas agregadas, nenhuma linha.
- Por que os outros bancos recusam essa consulta, e as formas que funcionam em todos.
A ideia: o pódio
Numa corrida, o MAX é o tempo do mais lento e o MIN é o do mais rápido. Mas repare na pergunta que todo mundo faz depois: "quem ganhou?". Saber o valor é uma coisa; saber de qual linha ele veio é outra. A primeira metade desta aula responde "quanto"; a segunda, "quem".
1. O maior número
SELECT MAX(preco) AS mais_caro FROM produtos;
- MAX
- A função do maior valor.
- (preco)
- A coluna onde procurar. Como as outras agregadas (menos o
COUNT), não aceita*. - AS mais_caro
- O apelido da coluna do resultado.
Com WHERE, o maior de um grupo. O item mais caro da Papelaria:
SELECT MAX(preco) AS mais_caro FROM produtos WHERE categoria_id = 1;
E com uma conta dentro: qual foi o maior valor de uma linha de item, quantidade × preço?
SELECT MAX(quantidade * preco_unitario) AS maior_item FROM itens_pedido;
569.97: três unidades da Caixa de Som Portátil, de 189.99, no mesmo pedido.
2. O maior texto e a data mais recente
Com texto, MAX devolve o último na ordem do SQLite, que é a ordem BINARY explicada em MIN() (maiúsculas antes das minúsculas, acentuadas depois do z):
SELECT MAX(nome) AS ultimo_nome,
MAX(cidade) AS ultima_cidade
FROM clientes;
Com datas no formato AAAA-MM-DD, o maior texto é a data mais recente. O último pedido e a última entrega:
SELECT MAX(data) AS ultimo_pedido,
MAX(entregue_em) AS ultima_entrega
FROM pedidos;
Repare: a última entrega (16/09/2026) é bem anterior ao último pedido (14/10/2026). Os pedidos mais novos ainda não chegaram: o entregue_em deles é NULL, e o MAX não conta NULL. E o cliente mais novo é quem tem o maior nascimento:
SELECT MAX(nascimento) AS nasceu_por_ultimo FROM clientes;
Experimente: o maior salário da loja é o da gerente. Qual é o maior salário tirando a gerente? Acrescente um WHERE que deixe de fora o cargo 'Gerente' (o "diferente" é <>).
SELECT MAX(salario) AS maior_salario FROM funcionarios;
SELECT MAX(salario) AS maior_salario FROM funcionarios WHERE cargo <> 'Gerente';
3. Quem é o mais caro? A coluna nua
O MAX(preco) diz que o mais caro custa 249.9. Mas qual produto é? A primeira ideia de todo mundo é pôr o nome ao lado:
SELECT nome, MAX(preco) AS preco FROM produtos;
Deu certo: o Jogo de Panelas 5 peças. Mas pense no que essa consulta pede. O MAX(preco) junta as 45 linhas numa só. E o nome? Ele não está dentro de nenhuma agregada; há 45 nomes e só uma linha no resultado. Qual deles deveria aparecer? Uma coluna assim, fora de qualquer agregada, tem um nome técnico: coluna nua (em inglês, bare column).
A regra do SQLite
A documentação do SQLite explica o que ele faz com uma coluna nua:
- Se a consulta tem exatamente um
MIN()ouMAX(), as colunas nuas vêm de uma linha que tem esse mínimo ou máximo. Por isso o nome saiu certo: veio da linha do preço 249.9. - Em qualquer outro caso, a coluna nua vem de uma linha qualquer, e não dá para saber qual.
A mesma documentação avisa: a maioria dos outros bancos não aceita colunas nuas e dá erro. Ela é uma extensão do SQLite, algo que só ele tem; o SQLite diz que isso é de propósito, "um recurso, e não um defeito". Funciona, mas tem três armadilhas.
Armadilha 1: empates
Qual pedido pagou o maior frete?
SELECT id, MAX(frete) AS maior_frete FROM pedidos;
Apareceu o pedido 8, e parece que só ele pagou 30.0. Não é verdade:
SELECT id, frete FROM pedidos WHERE frete = 30.0;
Onze pedidos empatam com o frete de 30.0. A coluna nua mostra um deles, sem avisar que existem outros, e a documentação diz que a escolha entre os empatados é arbitrária.
Armadilha 2: duas agregadas
Com um MAX e um MIN na mesma consulta, de qual linha vem o nome?
SELECT nome, MAX(preco), MIN(preco) FROM produtos;
SELECT nome, MIN(preco), MAX(preco) FROM produtos;
Só mudou a ordem das colunas, e o nome mudou: na primeira veio o Lápis (o mais barato), ao lado do preço 249.9; na segunda, as Panelas, ao lado do 1.15. Nas duas, o nome não combina com um dos preços. Com duas agregadas, o SQLite pega o nome da linha de uma delas, e a documentação diz que não dá para prever qual. Com AVG, SUM ou COUNT, que não apontam para linha nenhuma, a coluna nua vem de uma linha qualquer:
SELECT nome, AVG(preco) FROM produtos;
O Caderno Pautado não tem nada a ver com o preço médio. Esse resultado é lixo com cara de resposta.
Armadilha 3: nenhuma linha
SELECT nome, MAX(preco) FROM produtos WHERE preco > 1000;
Sem linhas, o MAX dá NULL, e a coluna nua também. Aqui saiu NULL, mas a documentação avisa que, nesse caso, a coluna nua pode ter qualquer valor.
4. As formas que funcionam em qualquer banco
Para responder "qual é o produto mais caro?" sem depender da regra do SQLite:
SELECT nome, preco FROM produtos ORDER BY preco DESC LIMIT 1;
SELECT nome, preco FROM produtos WHERE preco = (SELECT MAX(preco) FROM produtos);
A primeira ordena do mais caro para o mais barato (DESC, aula ORDER BY) e fica com a primeira linha. A segunda calcula o máximo numa subconsulta, o SELECT entre parênteses (aula Subconsultas), e depois busca as linhas que têm esse preço. A segunda é a única que mostra todos os empatados. Experimente trocar preco por frete e produtos por pedidos nela: saem os onze pedidos.
| Forma | Empates | Outros bancos | Use quando |
|---|---|---|---|
SELECT MAX(preco) | Não importa | Funciona | Só quer o valor |
SELECT nome, MAX(preco) | Mostra um, sem avisar | Erro na maioria | Consulta rápida no SQLite, sabendo que não há empate |
ORDER BY preco DESC LIMIT 1 | Mostra um | Funciona (TOP 1 no SQL Server) | Quer uma linha, e tanto faz qual dos empatados |
WHERE preco = (SELECT MAX(preco) …) | Mostra todos | Funciona | Quer a resposta completa. É a mais segura |
Quando dá errado
Se alguém guardar números como texto (por exemplo, ao importar uma planilha), o MAX compara letra por letra, como num dicionário:
SELECT MAX(column1) AS maior
FROM (VALUES ('9'), ('10'), ('100'));
O que aconteceu: o "maior" é 9. Como texto, '9' vem depois de '1…', assim como "zebra" vem depois de "abacate", não importa o tamanho. Com números de verdade, (9), (10), (100), sem aspas, o MAX dá 100.
Como consertar: guarde números em colunas numéricas, sem aspas (aula Os Tipos no SQLite). Se os dados já chegaram como texto, converta na consulta: MAX(CAST(coluna AS INTEGER)).
SELECT nome FROM produtos WHERE preco = MAX(preco);
A mensagem: misuse of aggregate function MAX(), "uso indevido da função agregada MAX()". O WHERE olha uma linha por vez; o máximo só existe depois de ver todas.
Como consertar: a subconsulta da seção 4.
SELECT MAX(*) FROM produtos;
A mensagem: wrong number of arguments to function MAX(). Diga a coluna: MAX(preco). (E, como o min, o max com dois ou mais valores, MAX(a, b), é uma função comum, linha por linha.)
A consulta SELECT nome, MAX(preco) FROM produtos; é recusada:
- PostgreSQL: a documentação diz que, numa consulta com agregadas ou
GROUP BY, a lista doSELECTnão pode citar colunas que não estão agrupadas, a não ser dentro de uma agregada (ou se a coluna depender da chave primária agrupada). - MySQL: o modo
ONLY_FULL_GROUP_BY, ligado por padrão, recusa a consulta. O exemplo da documentação oficial, com uma tabelat, mostra a mensagem:ERROR 1140 (42000): In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'mydb.t.name'; this is incompatible with sql_mode=only_full_group_by. Quando você sabe que tanto faz qual valor vem, o MySQL aceitaANY_VALUE(nome). - SQL Server: dá o erro 8120. Na documentação da Microsoft, o exemplo é
Column 'Sales.SalesOrderHeader.PurchaseOrderNumber' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.("a coluna é inválida na lista do SELECT porque não está numa função agregada nem no GROUP BY").
Por isso, neste guia, a coluna nua aparece só para você reconhecê-la. Nas suas consultas, prefira as formas da seção 4.
SELECT ANY_VALUE(nome), MAX(preco) FROM produtos;
Referência rápida
| Escrita | O que faz | Na Loja Aurora |
|---|---|---|
MAX(coluna) | O maior valor não NULL | MAX(preco) = 249.9 |
MAX(texto) | O último na ordem BINARY | MAX(cidade) = Teresina |
MAX(data) | A data mais recente (em AAAA-MM-DD) | MAX(data) = 2026-10-14 |
MAX(a * b) | O maior resultado da conta | 569.97 |
SELECT nome, MAX(preco) | Só SQLite: o nome vem da linha do máximo (um só, se houver empate) | Jogo de Panelas 5 peças |
WHERE x = (SELECT MAX(x) …) | Todas as linhas com o máximo, em qualquer banco | |
| Sem nenhuma linha | NULL |
| Mensagem do SQLite | Quer dizer |
|---|---|
misuse of aggregate function MAX() | MAX dentro do WHERE |
wrong number of arguments to function MAX() | MAX(*) ou MAX() vazio |
Exercícios
1. O maior estoque
Complete para achar o maior estoque entre os produtos:
2. Do mais caro para o mais barato
Complete para mostrar o produto mais caro numa forma que funciona em qualquer banco:
3. Qual é o MAX dos textos '8', '75' e '600'?
4. O que é uma "coluna nua"?
5. Onze pedidos têm o frete máximo, 30.0. Quantas linhas mostra SELECT id, MAX(frete) FROM pedidos; no SQLite?
6. O que acontece com SELECT nome, MAX(preco) FROM produtos; no PostgreSQL?
Missão final: quem ganha mais
Missão: a consulta mostra só o maior salário. Mude-a para mostrar o nome e o salário de quem ganha mais, numa linha só. Tente usar uma das formas que funcionam em qualquer banco.
SELECT MAX(salario) AS maior_salario FROM funcionarios;
SELECT nome, salario FROM funcionarios WHERE salario = (SELECT MAX(salario) FROM funcionarios);
Resumo
MAX(coluna)devolve o maior valor, o último de umORDER BYcrescente, sem contar osNULL.- Em texto, vale a ordem
BINARY; em datasAAAA-MM-DD, o maior é a mais recente. Números guardados como texto se comparam como texto:'9'>'100'. SELECT nome, MAX(preco)tem uma coluna nua. O SQLite a aceita: com um únicoMIN/MAX, ela vem da linha do extremo. É uma extensão só dele.- A coluna nua esconde empates, fica imprevisível com duas agregadas e vira lixo com
AVG/SUM/COUNT. - PostgreSQL, MySQL e SQL Server recusam a coluna nua com erro. Em qualquer banco funcionam
ORDER BY … DESC LIMIT 1eWHERE x = (SELECT MAX(x) …), e esta última mostra todos os empatados.