Como você obtém as linhas que contêm o valor máximo para cada conjunto agrupado?
Eu já vi algumas variações excessivamente complicadas nessa questão, e nenhuma com uma boa resposta. Eu tentei montar o exemplo mais simples possível:
Dada uma tabela como essa abaixo, com colunas de pessoa, grupo e faixa etária, como você obteria a pessoa mais velha de cada grupo? (Um empate dentro de um grupo deve dar o primeiro resultado alfabético)
Person | Group | Age
---
Bob | 1 | 32
Jill | 1 | 34
Shawn| 1 | 42
Jake | 2 | 29
Paul | 2 | 36
Laura| 2 | 39
Conjunto de resultados desejado:
Shawn | 1 | 42
Laura | 2 | 39
mysql
sql
greatest-n-per-group
Yarin
fonte
fonte
Respostas:
Existe uma maneira super simples de fazer isso no mysql:
Isso funciona porque no mysql você está autorizado a não agregada não-grupo-por colunas, caso em que o mysql retorna apenas a primeira linha. A solução é ordenar primeiro os dados de modo que, para cada grupo, a linha que você deseja seja primeiro e, em seguida, agrupe pelas colunas para as quais deseja o valor.
Você evita subconsultas complicadas que tentam encontrar o
max()
etc, e também os problemas de retornar várias linhas quando há mais de uma com o mesmo valor máximo (como as outras respostas fariam)Nota: Esta é uma solução somente para mysql . Todos os outros bancos de dados que eu conheço lançarão um erro de sintaxe SQL com a mensagem "colunas não agregadas não estão listadas no grupo por cláusula" ou similar. Como esta solução usa comportamento não documentado , o mais cauteloso pode querer incluir um teste para afirmar que continua funcionando se uma versão futura do MySQL alterar esse comportamento.
Atualização da versão 5.7:
Desde a versão 5.7, a
sql-mode
configuração incluiONLY_FULL_GROUP_BY
por padrão, portanto, para fazer isso funcionar, você não deve ter essa opção (edite o arquivo de opções do servidor para remover essa configuração).fonte
SELECT
cláusula e não é calculada usando uma função agregada.SELECT
cláusula não são funcionalmente dependentes dasGROUP BY
colunas. Se estiver configurado para aceitá-lo (`ONLY_FULL_GROUP_BY` está desativado), funciona como as versões anteriores (ou seja, os valores dessas colunas são indeterminados).GROUP BY
condensa em um registro, mas todos os campos serão escolhidos arbitrariamente a partir dos registros. Ele pode ser que o MySQL atualmente simplesmente escolhe sempre a primeira linha, mas poderia muito bem pegar qualquer outra linha ou mesmo valores de diferentes linhas em uma versão futura.A solução correta é:
Como funciona:
Ele corresponde a cada linha
o
com todas as linhas deb
ter o mesmo valor na colunaGroup
e um valor maior na colunaAge
. Qualquer linha queo
não tenha o valor máximo de seu grupo na colunaAge
corresponderá a uma ou mais linhas deb
.O
LEFT JOIN
faz-lo coincidir com a pessoa mais velha do grupo (incluindo as pessoas que estão sozinhas em seu grupo) com uma linha completa deNULL
s deb
( 'não maior idade no grupo').O uso
INNER JOIN
faz com que essas linhas não correspondam e são ignoradas.A
WHERE
cláusula mantém apenas as linhas comNULL
s nos campos extraídosb
. Eles são as pessoas mais velhas de cada grupo.Leituras adicionais
Esta solução e muitas outras são explicadas no livro Antipatterns SQL: Evitando as Armadilhas da Programação de Banco de Dados
fonte
o.Age = b.Age
, por exemplo, se Paul do grupo 2 estiver em 39 como Laura. No entanto, se não queremos esse comportamento, podemos fazer: #ON o.Group = b.Group AND (o.Age < b.Age or (o.Age = b.Age and o.id < b.id))
Você pode ingressar em uma subconsulta que puxa o
MAX(Group)
eAge
. Este método é portátil na maioria dos RDBMS.fonte
Group = 2, Age = 20
, onde , a subconsulta retornaria uma delas, mas aON
cláusula join corresponderia a ambas , portanto, você retornaria duas linhas com o mesmo grupo / idade, embora com valores diferentes para as outras colunas, ao invés de um.Minha solução simples para SQLite (e provavelmente MySQL):
No entanto, ele não funciona no PostgreSQL e talvez em outras plataformas.
No PostgreSQL você pode usar a cláusula DISTINCT ON :
fonte
Usando o método de classificação.
fonte
:=
antes - o que é isso?Não tenho certeza se o MySQL tem a função row_number. Nesse caso, você pode usá-lo para obter o resultado desejado. No SQL Server, você pode fazer algo semelhante a:
fonte
A solução da axiac é o que funcionou melhor para mim no final. No entanto, eu tinha uma complexidade adicional: um "valor máximo" calculado, derivado de duas colunas.
Vamos usar o mesmo exemplo: eu gostaria da pessoa mais velha de cada grupo. Se houver pessoas igualmente velhas, leve a pessoa mais alta.
Eu tive que executar a junção esquerda duas vezes para obter esse comportamento:
Espero que isto ajude! Acho que deveria haver uma maneira melhor de fazer isso ...
fonte
Minha solução funciona apenas se você precisar recuperar apenas uma coluna; no entanto, para minhas necessidades, foi a melhor solução encontrada em termos de desempenho (ela usa apenas uma única consulta!):
Ele usa GROUP_CONCAT para criar uma lista de concat ordenadas e, em seguida, faço a substring apenas para a primeira.
fonte
Eu tenho uma solução simples usando
WHERE IN
fonte
Usando CTEs - expressões comuns de tabela:
fonte
No Oracle, a consulta abaixo pode fornecer o resultado desejado.
fonte
fonte
Você também pode tentar
fonte
Eu não usaria Grupo como nome da coluna, pois é uma palavra reservada. No entanto, seguir o SQL funcionaria.
fonte
Esse método tem o benefício de permitir que você classifique por uma coluna diferente e não descarte os outros dados. É bastante útil em uma situação em que você está tentando listar pedidos com uma coluna para itens, listando os mais pesados primeiro.
Fonte: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
fonte
deixe o nome da tabela ser pessoas
fonte
Se o ID (e todos os coulmns) for necessário no mytable
fonte
É assim que eu estou recebendo as N linhas máximas por grupo no mysql
como funciona:
co.country = ci.country
) < 1
isso para 3 elementos -) <3co.id < ci.id
Exemplo completo aqui:
mysql seleciona n valores máximos por grupo
fonte