Como encontrar itens associados ao maior ou ao menor valor de uma lista no MySQL

Para listar itens e ainda associá-los aos maiores ou menores valores use funções do MySQL em conjunto com as funções MAX() e MIN().
Veja alguns exemplos em que 2 queries são combinadas para obter resultados mais significativos.
As funções MIN() e MAX() são úteis para encontrar os extremos de uma relação de valores. O problema é que, ás vezes, queremos também saber a que item na tabela o valor em questão se encontra associado.
em uma relação de clientes correntistas, podemos usar a função MAX() para determinar qual o maior valor de crédito. O problema a ser resolvido é o de determinar exatamente a quem pertence o tal valor.
Veja um exemplo prático, no qual vamos determinar qual o maior valor de créditos possuído por cliente, na tabela CadastroClientes:

SELECT MAX(CreditosCliente) FROM CadastroClientes;

Como você pode ver, abaixo, o maior valor é 4992:

+----------------------+
| MAX(CreditosCliente) |
+----------------------+
|                 4992 |
+----------------------+
1 row in set (0.01 sec)

E se você quiser saber a que cliente este valor está associado?
Uma das soluções mais óbvias seria a seguinte:

SELECT NomeCliente, MAX(CreditosCliente) FROM CadastroClientes WHERE CreditosCliente = MAX(CreditosClente);

ERROR 1111 (HY000): Invalid use of group function

Infelizmente, ela não funciona.
Por mais que a lógica desta query pareça boa (ela não é), o fato é que funções agregadas, tais como MIN() e MAX(), não podem ser usadas em cláusulas WHERE.
A intenção da consulta é determinar qual registro tem o maior valor de crédito e exibir o nome do cliente associado a este valor.
O problema é que enquanto eu e você entendemos a consulta, pro MySQL ela não faz sentido algum.
Ela não funcionou porque, o MySQL usa a cláusla WHERE para determinar quais registros selecionar, mas só conhece o valor de uma função agregada depois de selecionar os registros a partir dos quais o valor da função é determinado.
A declaração se contradiz, portanto.
A solução, abaixo, é uma gambiarra e, embora chegue à conclusão correta, não responde diretamente ao enunciado:

SELECT NomeCliente, CreditosCliente FROM CadastroClientes ORDER BY CreditosCliente DESC LIMIT 1;
+------------------+-----------------+
| NomeCliente      | CreditosCliente |
+------------------+-----------------+
| Camille Thornton |            4992 |
+------------------+-----------------+
1 row in set (0.00 sec)

Você pode solucionar o problema usando uma abordagem em 2 estágios ou um subselect.
No primeiro estágio, atribuo o maior valor de créditos da tabela à variável CreditoMax:

SELECT @CreditoMax := MAX(CreditosCliente) FROM CadastroClientes;
+-------------------------------------+
| @CreditoMax := MAX(CreditosCliente) |
+-------------------------------------+
|                                4992 |
+-------------------------------------+
1 row in set (0.00 sec)

No segundo estágio, já tenho condições de criar uma consulta que encontre o valor máximo, relacionado ao cliente:

SELECT NomeCliente AS Cliente, @CreditoMax AS "Créditos" FROM CadastroClientes WHERE CreditosCliente = @CreditoMax;
+------------------+-----------+
| Cliente          | Créditos  |
+------------------+-----------+
| Camille Thornton |      4992 |
+------------------+-----------+
1 row in set (0.00 sec)

Nas versões mais atuais do MySQL, é possível resolver o problema com uma subselect, em apenas uma linha de código:

SELECT NomeCliente AS Cliente, CreditosCliente AS "Créditos" FROM CadastroClientes WHERE CreditosCliente = (SELECT MAX(CreditosCliente) FROM CadastroClientes);
+------------------+----------+
| Cliente          | Créditos |
+------------------+----------+
| Camille Thornton |     4992 |
+------------------+----------+
1 row in set (0.00 sec);

Mais um exemplo

No próximo exemplo, vou usar uma tabela que contém a Bíblia.
O objetivo é estabelecer qual o menor versículo presente no livro.
Primeiro, vamos estabelecer quantos caracteres tem o menor versículo da bíblia:

SELECT MIN(LENGTH(textover)) FROM bibliakjv;
+-----------------------+
| MIN(LENGTH(textover)) |
+-----------------------+
|                    13 |
+-----------------------+
1 row in set (0.02 sec)

Que tal uma resposta mais completa — que mostre a quantidade de caracteres do menor versículo da bíblia e o texto do mesmo?
Novamente, vejamos como solucionar o problema em 2 estágios (definindo uma variável e operando com ela, em seguida):

SELECT @MenorVerso := MIN(LENGTH(textover)) FROM bibliakjv;
+--------------------------------------+
| @MenorVerso := MIN(LENGTH(textover)) |
+--------------------------------------+
|                                   13 |
+--------------------------------------+
1 row in set (0.01 sec)

Uma vez atribuído o valor da menor quantidade de caracteres à variável @MenorVerso, vamos usá-lo na próxima consulta:

SELECT numliv AS "Livro", numcap AS "Capítulo", numver AS "Versículo", textover AS "Texto do versículo" FROM bibliakjv WHERE LENGTH(textover) = @MenorVerso\G
*************************** 1. row ***************************
             Livro: 5
          Capítulo: 5
         Versículo: 17
Texto do versículo: Não matarás. 
Núm. de caracteres: 13
1 row in set (0.02 sec)

Note que, na minha versão da Bíblia, há um espaço a mais no versículo, logo após o ponto final — que é contado e, portanto, dá 13 caracteres ao versículo. Paciência… 😉
Se você prefere uma solução usando uma subquery, lá vai:

SELECT numliv AS "Livro", numcap AS "Capítulo", numver AS "Versículo", textover AS "Texto do versículo", @MenorVerso AS "Núm. de caracteres" FROM bibliakjv WHERE LENGTH(textover) = (SELECT MIN(LENGTH(textover)) FROM bibliakjv)\G
*************************** 1. row ***************************
             Livro: 5
          Capítulo: 5
         Versículo: 17
Texto do versículo: Não matarás.
Núm. de caracteres: 13
1 row in set (0.05 sec)

Publicado por

Elias Praciano

Autor de tecnologia (livre, de preferência), apaixonado por programação e astronomia.
Fã de séries, como “Rick and Morty” e “BoJack Horseman”.
Me siga no Twitter e vamos trocar ideias!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *