A seguinte consulta:
SELECT
year, id, rate
FROM h
WHERE year BETWEEN 2000 AND 2009
AND id IN (SELECT rid FROM table2)
GROUP BY id, year
ORDER BY id, rate DESC
rendimentos:
year id rate
2006 p01 8
2003 p01 7.4
2008 p01 6.8
2001 p01 5.9
2007 p01 5.3
2009 p01 4.4
2002 p01 3.9
2004 p01 3.5
2005 p01 2.1
2000 p01 0.8
2001 p02 12.5
2004 p02 12.4
2002 p02 12.2
2003 p02 10.3
2000 p02 8.7
2006 p02 4.6
2007 p02 3.3
O que eu gostaria é apenas os 5 principais resultados para cada ID:
2006 p01 8
2003 p01 7.4
2008 p01 6.8
2001 p01 5.9
2007 p01 5.3
2001 p02 12.5
2004 p02 12.4
2002 p02 12.2
2003 p02 10.3
2000 p02 8.7
Existe uma maneira de fazer isso usando algum tipo de LIMIT como modificador que funciona dentro do GROUP BY?
sql
mysql
greatest-n-per-group
ranking
Wells
fonte
fonte
LIMIT
cláusula. Aqui está um artigo que explica o problema em detalhes: Como selecionar a primeira / menor / máxima linha por grupo no SQL É um bom artigo - ele apresenta uma solução elegante, porém ingênua, para o problema "Top N por grupo" e depois gradualmente melhora nisso.Respostas:
Você pode usar a função agregada GROUP_CONCAT para obter todos os anos em uma única coluna, agrupada
id
e ordenada porrate
:Resultado:
E então você pode usar FIND_IN_SET , que retorna a posição do primeiro argumento dentro do segundo, por exemplo.
Usando uma combinação de
GROUP_CONCAT
eFIND_IN_SET
, e filtrar pela posição retornado por FIND_IN_SET, você poderia, então, usar essa consulta que retorna apenas os primeiros 5 anos para cada id:Por favor, veja violino aqui .
Observe que se mais de uma linha puder ter a mesma taxa, considere usar GROUP_CONCAT (taxa DISTINCT de ORDER BY rate) na coluna de taxa, em vez da coluna do ano.
O comprimento máximo da sequência retornada por GROUP_CONCAT é limitado, portanto, isso funcionará bem se você precisar selecionar alguns registros para cada grupo.
fonte
SET SESSION group_concat_max_len = <maximum length>;
No caso do OP, um não problema (já que o padrão é 1024), mas a título de exemplo, group_concat_max_len deve ser de pelo menos 25: 4 (máximo comprimento de uma sequência de anos) + 1 (caractere separador), vezes 5 (primeiros 5 anos). As cadeias são truncadas em vez de gerar um erro; portanto, observe avisos como1054 rows in set, 789 warnings (0.31 sec)
.FIND_IN_SET()
. Eu tentei,FIND_IN_SET() =2
mas não mostrando o resultado conforme o esperado.A consulta original usou variáveis do usuário e
ORDER BY
em tabelas derivadas; o comportamento de ambas as peculiaridades não é garantido. Resposta revisada da seguinte forma.No MySQL 5.x, você pode usar a classificação do pobre homem sobre a partição para obter o resultado desejado. Apenas junte a tabela externamente e, para cada linha, conte o número de linhas menor que ele. No caso acima, linha menor é aquela com taxa mais alta:
Demonstração e resultado :
Observe que se as taxas tiverem vínculos, por exemplo:
A consulta acima retornará 6 linhas:
Mude para
HAVING COUNT(DISTINCT l.rate) < 5
para obter 8 linhas:Ou mude para
ON t.id = l.id AND (t.rate < l.rate OR (t.rate = l.rate AND t.pri_key > l.pri_key))
para obter 5 linhas:No MySQL 8 ou posterior, basta usar as funções
RANK
,DENSE_RANK
ouROW_NUMBER
:fonte
WHERE rank <=5
? Pela primeira vez, não estou conseguindo 5 linhas de cada ID, mas depois disso eu consigo o que você disse.SET
declaração (consulte a primeira consulta). É necessário.ORDER BY
tabela derivada pode, e geralmente será, ignorada. Isso derrota o objetivo. Grupo eficiente é encontrado aqui .ORDER BY
em deliverd / subconsultas como essa. Essa é a razão pela qual MySQL moderna / versões MariaDB ignorar oORDER BY
na subconsulta sem usarLIMIT
, eu acredito ANSI / ISO Standards SQL 2008/2011/2016 as marcasORDER BY
em deliverd / subconsultas legais quando usá-lo em combinação comFETCH FIRST n ROWS ONLY
Para mim, algo como
funciona perfeitamente. Nenhuma consulta complicada.
por exemplo: obtenha o primeiro 1 para cada grupo
fonte
Não, você não pode LIMITAR subconsultas arbitrariamente (você pode fazê-lo de forma limitada nos MySQLs mais recentes, mas não para 5 resultados por grupo).
Essa é uma consulta do tipo máximo de grupo, que não é trivial para fazer no SQL. Existem várias maneiras de lidar com o que pode ser mais eficiente em alguns casos, mas para o top-n em geral, você deve considerar a resposta de Bill a uma pergunta anterior semelhante.
Como na maioria das soluções para esse problema, ele pode retornar mais de cinco linhas se houver várias linhas com o mesmo
rate
valor; portanto, você ainda precisará de uma quantidade de pós-processamento para verificar isso.fonte
Isso requer uma série de subconsultas para classificar os valores, limitá-los e executar a soma ao agrupar
fonte
Tente o seguinte:
fonte
A subconsulta é quase idêntica à sua consulta. Somente a mudança está adicionando
fonte
ROW_NUMBER()
).row_number()
está disponível .Construa as colunas virtuais (como RowID no Oracle)
mesa:
dados:
SQL assim:
se excluir a cláusula where em t3, será exibida assim:
GET "TOP N Record" -> adicione o "rownum <= 3" na cláusula where (a cláusula where de t3);
ESCOLHA "o ano" -> adicione os "ENTRE 2000 E 2009" na cláusula where (a cláusula where de t3);
fonte
Demorei um pouco para trabalhar, mas acho que minha solução seria algo para compartilhar, pois parece elegante e rápido.
Observe que este exemplo é especificado para o objetivo da pergunta e pode ser modificado facilmente para outros fins semelhantes.
fonte
A seguinte postagem: sql: selecionar o registro N superior por grupo descreve a maneira complicada de conseguir isso sem subconsultas.
Ele aprimora outras soluções oferecidas aqui por:
No entanto, não é bonito. Uma boa solução seria possível se o Window Functions (também conhecido como Analytic Functions) estivesse ativado no MySQL - mas não é. O truque usado no referido post utiliza GROUP_CONCAT, que às vezes é descrito como "Funções de Janela do pobre homem para MySQL".
fonte
para aqueles como eu que tiveram tempo limite de consultas. Fiz o abaixo para usar limites e qualquer outra coisa de um grupo específico.
ele percorre uma lista de domínios e insere apenas um limite de 200 cada
fonte
Tente o seguinte:
fonte
Por favor, tente abaixo o procedimento armazenado. Eu já verifiquei. Estou obtendo resultado adequado, mas sem usar
groupby
.fonte