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.

Você vai aprender
  • Achar o maior valor de uma coluna, de uma conta ou de um grupo com WHERE.
  • MAX com 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() ou MAX(), 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.

FormaEmpatesOutros bancosUse quando
SELECT MAX(preco)Não importaFuncionaSó quer o valor
SELECT nome, MAX(preco)Mostra um, sem avisarErro na maioriaConsulta rápida no SQLite, sabendo que não há empate
ORDER BY preco DESC LIMIT 1Mostra umFunciona (TOP 1 no SQL Server)Quer uma linha, e tanto faz qual dos empatados
WHERE preco = (SELECT MAX(preco) …)Mostra todosFuncionaQuer a resposta completa. É a mais segura

Quando dá errado

Números guardados como texto

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

MAX no WHERE
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.

MAX(*)
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.)

Nos outros bancos: a coluna nua dá erro

A consulta SELECT nome, MAX(preco) FROM produtos; é recusada:

  • PostgreSQL: a documentação diz que, numa consulta com agregadas ou GROUP BY, a lista do SELECT nã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 tabela t, 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 aceita ANY_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

EscritaO que fazNa Loja Aurora
MAX(coluna)O maior valor não NULLMAX(preco) = 249.9
MAX(texto)O último na ordem BINARYMAX(cidade) = Teresina
MAX(data)A data mais recente (em AAAA-MM-DD)MAX(data) = 2026-10-14
MAX(a * b)O maior resultado da conta569.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 linhaNULL
Mensagem do SQLiteQuer 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:

SELECT (estoque) FROM produtos;

2. Do mais caro para o mais barato

Complete para mostrar o produto mais caro numa forma que funciona em qualquer banco:

SELECT nome, preco FROM produtos ORDER BY preco LIMIT 1;

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 um ORDER BY crescente, sem contar os NULL.
  • Em texto, vale a ordem BINARY; em datas AAAA-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 único MIN/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 1 e WHERE x = (SELECT MAX(x) …), e esta última mostra todos os empatados.