La siguiente 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
rendimientos:
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
Lo que me gustaría son solo los 5 mejores 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
¿Hay alguna manera de hacer esto usando algún tipo de modificador como LIMIT que funcione dentro de GROUP BY?
LIMIT
cláusula. Aquí hay un artículo que explica el problema en detalle: Cómo seleccionar la primera / mínima / máxima fila por grupo en SQL Es un buen artículo: presenta una solución elegante pero ingenua para el problema "Top N por grupo", y luego gradualmente mejora en ello.Respuestas:
Puede usar la función agregada GROUP_CONCAT para obtener todos los años en una sola columna, agrupada
id
y ordenada porrate
:Resultado:
Y luego podría usar FIND_IN_SET , que devuelve la posición del primer argumento dentro del segundo, por ejemplo.
Usando una combinación de
GROUP_CONCAT
yFIND_IN_SET
, y filtrando por la posición devuelta por find_in_set, puede usar esta consulta que devuelve solo los primeros 5 años para cada id:Por favor, vea el violín aquí .
Tenga en cuenta que si más de una fila puede tener la misma tasa, debería considerar usar GROUP_CONCAT (tasa DISTINCT ORDER BY) en la columna de tasa en lugar de la columna del año.
La longitud máxima de la cadena devuelta por GROUP_CONCAT es limitada, por lo que funciona bien si necesita seleccionar algunos registros para cada grupo.
fuente
SET SESSION group_concat_max_len = <maximum length>;
en el caso del OP, un problema (ya que el valor predeterminado es 1024), pero a modo de ejemplo, group_concat_max_len debe ser al menos 25: 4 (máximo longitud de una cadena de año) + 1 (carácter separador), multiplicado por 5 (primeros 5 años). Las cadenas se truncan en lugar de arrojar un error, así que esté atento a advertencias como1054 rows in set, 789 warnings (0.31 sec)
.FIND_IN_SET()
. IntentéFIND_IN_SET() =2
pero no mostraba el resultado esperado.La consulta original utilizaba variables de usuario y
ORDER BY
en tablas derivadas; El comportamiento de ambas peculiaridades no está garantizado. Respuesta revisada de la siguiente manera.En MySQL 5.x puede usar el rango de pobre sobre la partición para lograr el resultado deseado. Solo unir externamente la tabla consigo mismo y para cada fila, contar el número de filas menor que él. En el caso anterior, la fila menor es la que tiene la tasa más alta:
Demostración y resultado :
Tenga en cuenta que si las tasas tienen vínculos, por ejemplo:
La consulta anterior devolverá 6 filas:
Cambie a
HAVING COUNT(DISTINCT l.rate) < 5
para obtener 8 filas:O cambie a
ON t.id = l.id AND (t.rate < l.rate OR (t.rate = l.rate AND t.pri_key > l.pri_key))
para obtener 5 filas:En MySQL 8 o posterior solo use las funciones
RANK
,DENSE_RANK
oROW_NUMBER
:fuente
WHERE rank <=5
? Por primera vez no obtengo 5 filas de cada ID, pero después puedo obtener lo que dijiste.SET
declaración (consulte la primera consulta). Es necesario.ORDER BY
de la tabla derivada puede, y a menudo lo será, ignorarse. Esto derrota la meta. Eficiente en cuanto a grupos se encuentran aquí .ORDER BY
en entregas / subconsultas como esa ... Esa es la razón por la cual las versiones modernas de MySQL / MariaDB ignoran la consultaORDER BY
secundaria sin usarlaLIMIT
, creo que las normas ANSI / ISO SQL 2008/2011/2016 hacen que lasORDER BY
entregas / subconsultas sean legales cuando se usa en combinación conFETCH FIRST n ROWS ONLY
Para mi algo como
funciona perfectamente. No hay consulta complicada.
por ejemplo: obtenga el primer 1 para cada grupo
fuente
No, no puede LIMITAR las subconsultas de forma arbitraria (puede hacerlo de forma limitada en MySQL más recientes, pero no para obtener 5 resultados por grupo).
Esta es una consulta de tipo máximo grupal, que no es trivial de hacer en SQL. Hay varias formas de abordar lo que puede ser más eficiente en algunos casos, pero para top-n en general, querrá ver la respuesta de Bill a una pregunta anterior similar.
Como con la mayoría de las soluciones a este problema, puede devolver más de cinco filas si hay varias filas con el mismo
rate
valor, por lo que aún puede necesitar una cantidad de procesamiento posterior para verificarlo.fuente
Esto requiere una serie de subconsultas para clasificar los valores, limitarlos y luego realizar la suma mientras se agrupa
fuente
Prueba esto:
fuente
La subconsulta es casi idéntica a su consulta. Solo el cambio es agregar
fuente
ROW_NUMBER()
).row_number()
está disponible .Construya las columnas virtuales (como RowID en Oracle)
mesa:
datos:
SQL como este:
si elimina la cláusula where en t3, se muestra así:
OBTENGA "TOP N Record" -> agregue el "rownum <= 3" en la cláusula where (la cláusula where de t3);
ELIJA "el año" -> agregue "ENTRE 2000 Y 2009" en la cláusula where (la cláusula where de t3);
fuente
Tomó algo de trabajo, pero creo que mi solución sería algo para compartir, ya que parece elegante y bastante rápido.
Tenga en cuenta que este ejemplo se especifica para el propósito de la pregunta y se puede modificar con bastante facilidad para otros fines similares.
fuente
La siguiente publicación: sql: selección del registro N superior por grupo describe la forma complicada de lograr esto sin subconsultas.
Mejora en otras soluciones ofrecidas aquí por:
Sin embargo, no es bonito. Una buena solución sería posible si las Funciones de Windows (también conocidas como Funciones Analíticas) estuvieran habilitadas en MySQL, pero no lo están. El truco utilizado en dicha publicación utiliza GROUP_CONCAT, que a veces se describe como "Funciones de ventana del pobre para MySQL".
fuente
para aquellos como yo que tenían dudas sobre el tiempo de espera. Hice lo siguiente para usar límites y cualquier otra cosa por un grupo específico.
recorre una lista de dominios y luego inserta solo un límite de 200 cada uno
fuente
Prueba esto:
fuente
Por favor, intente debajo del procedimiento almacenado. Ya lo he verificado. Estoy obteniendo el resultado adecuado pero sin usar
groupby
.fuente