2009-03-20 71 views
47

tengo esta consulta MySQL:Calcular un total acumulado en MySQL

SELECT DAYOFYEAR(`date`) AS d, COUNT(*) 
FROM `orders` 
WHERE `hasPaid` > 0 
GROUP BY d 
ORDER BY d 

que devuelve algo como esto:

d | COUNT(*) | 
20 | 5  | 
21 | 7  | 
22 | 12  | 
23 | 4  | 

Lo que realmente me gusta es otra columna en el extremo para mostrar la total acumulado:

d | COUNT(*) | ??? | 
20 | 5  | 5 | 
21 | 7  | 12 | 
22 | 12  | 24 | 
23 | 4  | 28 | 

¿Esto es posible?

+0

posible duplicado de [Crear una columna de suma acumulativa en MySQL] (http://stackoverflow.com/questions/2563918/create-a-cumulative-sum-column-in-mysql) – Ztyx

+1

@Ztyx Se realizó su pregunta vinculada más de un año después. Entonces, sería más bien al revés. –

Respuesta

87

Quizás una solución más simple para usted y evite que la base de datos tenga que hacer un montón de consultas. Esto ejecuta solo una consulta y luego hace un poco de matemática sobre los resultados en una sola pasada.

SET @runtot:=0; 
SELECT 
    q1.d, 
    q1.c, 
    (@runtot := @runtot + q1.c) AS rt 
FROM 
    (SELECT 
     DAYOFYEAR(`date`) AS d, 
     COUNT(*) AS c 
    FROM `orders` 
    WHERE `hasPaid` > 0 
    GROUP BY d 
    ORDER BY d) AS q1 

Esto le dará una columna RT adicional (total acumulado). No se pierda la instrucción SET en la parte superior para inicializar primero la variable total en ejecución o simplemente obtendrá una columna de valores NULOS.

+1

que funciona brillantemente! Ver el 'EXPLAIN' en esto muestra que es mucho más eficiente que la respuesta previamente aceptada – nickf

+0

El punto clave es usar una subconsulta. Esto lo hace confiable en consultas complejas que involucran múltiples tablas o agregaciones. –

+0

Para aquellos que quieran hacer algo como esto con las funciones básicas de MySQL de PHP, asegúrese de ejecutar la primera línea por separado (pero aún antes de la 2da). –

1

Yo diría que esto es imposible, cada fila resultante debería ser independiente. Use un lenguaje de programación para conseguir estos valores

+0

Dada la naturaleza de las matemáticas relacionales, y el hecho de que está usando group by, incluso si mysql tiene algún hack para hacer esto posible, sería menos complicado simplemente hacerlo en un lenguaje de programación como sugiere Sergej. –

+6

No estoy de acuerdo.La división de las tareas de procesamiento entre la base de datos y la capa de aplicación es problemática desde una perspectiva de reutilización y mantenimiento. Si desea utilizar esta información en diferentes lugares, tal vez en un informe y en una pantalla, tendrá que duplicar la lógica de totales acumulados. – cdonner

+0

+1 tienes razón: esto sería más fácil y mejor en general en la lógica de programación: estaba tratando de ver si había alguna función mágica para hacerlo. – nickf

9
SELECT 
    DAYOFYEAR(O.`date`) AS d, 
    COUNT(*), 
    (select count(*) from `orders` 
     where DAYOFYEAR(`date`) <= d and `hasPaid` > 0) 
FROM 
    `orders` as O 
WHERE 
    O.`hasPaid` > 0 
GROUP BY d 
ORDER BY d 

Esto requerirá algunos ajustes sintáctica (no tengo MySQL para probarlo), pero que muestra la idea. La subconsulta solo tiene que volver y agregar todo lo nuevo que ya incluyó en la consulta externa, y tiene que hacer eso para cada fila.

Eche un vistazo a this question para saber cómo usar las uniones para lograr lo mismo.

Para abordar las preocupaciones sobre la degradación del rendimiento con datos crecientes: Dado que hay un máx. 366 días en un año, y supongo que no está ejecutando esta consulta en varios años, la subconsulta se evaluará hasta 366 veces. Con los índices adecuados en la fecha y la bandera hasPaid, estarás bien.

+0

gracias - esto funciona perfectamente como está. – nickf

+1

Tenga en cuenta que esto será extremadamente lento en las bases de datos grandes, promedio y algunas pequeñas, porque necesita hacer tantas consultas adicionales como filas en el resultado –

+0

de acuerdo. Hice +1 en esta respuesta porque es inteligente, y todos hemos utilizado soluciones como esta cuando fue necesario, pero también todos sabemos que hay un costo. Depende de dónde necesites el recuento de carreras. Para la lógica de negocios? Entonces quizás haga esto en DB. Para la vista? Hazlo en código. –

1

A menos que no tenga otra opción que hacer esto en sql, sumaría los resultados en el lenguaje de programación que realiza la consulta. Un anidamiento como este se volverá muy lento a medida que la mesa crezca.

+0

El rendimiento crecerá con el tamaño de la tabla, pero no agresivamente ya que el valor se calcula y se conserva. Otros enfoques que dependen de una sub selección serán más caros. – Brendan

0

Puede hackear esto usando la instrucción Cross Join o algunas combinaciones, pero se ralentizará con cualquier conjunto de datos de gran tamaño, probablemente sea mejor hacerlo en un procesador posterior a la consulta; ya sea cursor de en código de cliente

0

Este es uno de los únicos lugares en los cursores son más rápidos que una base se desarrolló en las consultas, si el rendimiento es crítico me sea

  • Haga esto fuera de MySql o
  • Use MySql 5 Cursors
Cuestiones relacionadas