MySQL Query GROUP BY día / mes / año

649

¿Es posible hacer una consulta simple para contar cuántos registros tengo en un período de tiempo determinado, como un año, mes o día, teniendo un TIMESTAMPcampo, como:

SELECT COUNT(id)
FROM stats
WHERE record_date.YEAR = 2009
GROUP BY record_date.YEAR

O incluso:

SELECT COUNT(id)
FROM stats
GROUP BY record_date.YEAR, record_date.MONTH

Tener una estadística mensual.

¡Gracias!

Fernando Barrocal
fuente
1
¿Supongo que se supone que debe estar GROUP BY record_date.MONTHen tu primer fragmento de código?
chiccodoro

Respuestas:

1012
GROUP BY YEAR(record_date), MONTH(record_date)

Consulte las funciones de fecha y hora en MySQL.

codelogic
fuente
27
Es posible que desee agregar una columna adicional para mayor claridad en algunos casos, como cuando los registros abarcan varios años. SELECT COUNT (event_id), DATE_FORMAT (event_start, '% Y /% m')
Ric
Ejemplo completo simple: SELECT count(*), record_date FROM anytable WHERE anytable.anycolumn = 'anycondition' GROUP BY YEAR(record_date), month(record_date);nota: record_date es un tipo de fecha TIMESTAMP
renedet el
Probablemente valga la pena mencionar que esto no se ejecutó en mi MySQL 5.7 con una columna COUNT con alias (sin error, obtuve cero resultados). Cuando cambié para seleccionar esos campos con alias, pude agrupar por el alias. Esta es la imagen estándar de Docker MySQL 5.7 que se ejecuta en un entorno local, así que no tengo idea de por qué no produjo errores ni devolvió resultados.
MrMesees
3
Oh dios, si supiera esto antes ... tantas líneas de PHP para hacer algo que mysql puede hacer en una línea.
noches del
231
GROUP BY DATE_FORMAT(record_date, '%Y%m')

Nota (principalmente, para potenciales downvoters). Actualmente, esto puede no ser tan eficiente como otras sugerencias. Aún así, lo dejo como una alternativa, y también una, que puede servir para ver qué tan rápidas son otras soluciones. (Porque realmente no se puede distinguir rápido de lento hasta que ve la diferencia). Además, a medida que pasa el tiempo, se podrían hacer cambios en el motor de MySQL con respecto a la optimización para hacer esta solución, en algunos casos (tal vez, no tan distante) en el futuro, para llegar a ser bastante comparable en eficiencia con la mayoría de los demás.

Andriy M
fuente
3
Tengo la sensación de que esto no funcionaría bien porque una función de formato no podría usar un índice en la columna de fecha.
Sonny
@Stv: Quizás quieras considerar la respuesta de @ fu-chi entonces. Por lo que puedo decir, las expresiones de agrupación tanto en esa respuesta como en la mía evalúan lo mismo, pero EXTRACT()pueden ser más eficientes que DATE_FORMAT(). (Sin embargo, no tengo un MySQL para realizar las pruebas adecuadas).
Andriy M
45

prueba este

SELECT COUNT(id)
FROM stats
GROUP BY EXTRACT(YEAR_MONTH FROM record_date)

La función EXTRACT (unit FROM date) es mejor ya que se utiliza menos agrupación y la función devuelve un valor numérico.

La condición de comparación al agrupar será más rápida que la función DATE_FORMAT (que devuelve un valor de cadena). Intente utilizar un campo de función que devuelva un valor que no sea de cadena para la condición de comparación de SQL (WHERE, HAVING, ORDER BY, GROUP BY).

fu-chi
fuente
43

Intenté usar la declaración 'WHERE' anterior, pensé que era correcta ya que nadie la corrigió pero me equivoqué; Después de algunas búsquedas descubrí que esta es la fórmula correcta para la instrucción WHERE, por lo que el código se convierte así:

SELECT COUNT(id)  
FROM stats  
WHERE YEAR(record_date) = 2009  
GROUP BY MONTH(record_date)
dimazaid
fuente
30

Si su búsqueda ha durado varios años y aún desea agruparse mensualmente, le sugiero:

versión 1:

SELECT SQL_NO_CACHE YEAR(record_date), MONTH(record_date), COUNT(*)
FROM stats
GROUP BY DATE_FORMAT(record_date, '%Y%m')

versión # 2 (más eficiente) :

SELECT SQL_NO_CACHE YEAR(record_date), MONTH(record_date), COUNT(*)
FROM stats
GROUP BY YEAR(record_date)*100 + MONTH(record_date)

Comparé estas versiones en una mesa grande con 1,357,918 filas (), y la segunda versión parece tener mejores resultados.

version1 (promedio de 10 ejecuta) : 1.404 segundos
version2 (promedio de 10 ejecuta) : 0.780 segundos

( SQL_NO_CACHEclave agregada para evitar que MySQL CACHE a consultas).

mr.baby123
fuente
1
Considere incluir la sugerencia de @ fu-chi en sus pruebas, puede resultar aún más eficiente. Además, probaste GROUP BY YEAR(record_date)*100 + MONTH(record_date), pero ¿por qué no probar GROUP BY YEAR(record_date), MONTH(record_date)también?
Andriy M
2
Si usa COUNT (1) en lugar de COUNT (*) será aún más rápido y los datos de resultados son los mismos.
Pa0l0
2
¿Qué es eso *100en la versión # 2? Gracias por adelantado.
Avión
1
*100aYEAR(record_date)*100 + MONTH(record_date) == DATE_FORMAT(record_date, '%Y%m')
Phu Duy
17

Si desea agrupar por fecha en MySQL, utilice el siguiente código:

 SELECT COUNT(id)
 FROM stats
 GROUP BY DAYOFMONTH(record_date)

Espero que esto ahorre algo de tiempo para los que van a encontrar este hilo.

Haijerome
fuente
66
Es importante tener en cuenta que también necesitaría agrupar MONTH(record_date)para tener en cuenta varios meses.
Webnet
14

Si desea filtrar registros para un año en particular (por ejemplo, 2000), optimice la WHEREcláusula de esta manera:

SELECT MONTH(date_column), COUNT(*)
FROM date_table
WHERE date_column >= '2000-01-01' AND date_column < '2001-01-01'
GROUP BY MONTH(date_column)
-- average 0.016 sec.

En vez de:

WHERE YEAR(date_column) = 2000
-- average 0.132 sec.

Los resultados se generaron en una tabla que contiene 300k filas e índice en la columna de fecha.

En cuanto a la GROUP BYcláusula, probé las tres variantes contra la tabla mencionada anteriormente; aquí están los resultados:

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY YEAR(date_column), MONTH(date_column)
-- codelogic
-- average 0.250 sec.

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY DATE_FORMAT(date_column, '%Y%m')
-- Andriy M
-- average 0.468 sec.

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY EXTRACT(YEAR_MONTH FROM date_column)
-- fu-chi
-- average 0.203 sec.

El último es el ganador.

Salman A
fuente
10

Solución completa y simple con un rendimiento similar pero más corto y una alternativa más flexible actualmente activa:

SELECT COUNT(*) FROM stats
-- GROUP BY YEAR(record_date), MONTH(record_date), DAYOFMONTH(record_date)
GROUP BY DATE_FORMAT(record_date, '%Y-%m-%d')
Cees Timmerman
fuente
7

Si desea obtener estadísticas mensuales con recuentos de filas por mes de cada año ordenado por el último mes, intente esto:

SELECT count(id),
      YEAR(record_date),
      MONTH(record_date) 
FROM `table` 
GROUP BY YEAR(record_date),
        MONTH(record_date) 
ORDER BY YEAR(record_date) DESC,
        MONTH(record_date) DESC
usuario3019799
fuente
7

Puede hacer esto simplemente con la función Mysql DATE_FORMAT () en GROUP BY. Es posible que desee agregar una columna adicional para mayor claridad en algunos casos, como cuando los registros abarcan varios años y luego se produce el mismo mes en diferentes años. Aquí hay tantas opciones que puede personalizar. Lea esto antes de comenzar. Espero que sea de mucha ayuda para ti. Aquí hay una muestra de consulta para su comprensión

SELECT
    COUNT(id),
    DATE_FORMAT(record_date, '%Y-%m-%d') AS DAY,
    DATE_FORMAT(record_date, '%Y-%m') AS MONTH,
    DATE_FORMAT(record_date, '%Y') AS YEAR

FROM
    stats
WHERE
    YEAR = 2009
GROUP BY
    DATE_FORMAT(record_date, '%Y-%m-%d ');
Faisal
fuente
4

La siguiente consulta funcionó para mí en Oracle Database 12c Release 12.1.0.1.0

SELECT COUNT(*)
FROM stats
GROUP BY 
extract(MONTH FROM TIMESTAMP),
extract(MONTH FROM TIMESTAMP),
extract(YEAR  FROM TIMESTAMP);
Minisha
fuente
2

Prefiero optimizar la selección de grupo de un año así:

SELECT COUNT(*)
  FROM stats
 WHERE record_date >= :year 
   AND record_date <  :year + INTERVAL 1 YEAR;

De esta manera, puede vincular el año de una vez, por ejemplo '2009', con un parámetro con nombre y no tiene que preocuparse por agregar '-01-01'o pasar por '2010'separado.

Además, como presumiblemente solo estamos contando filas y idnunca NULL, prefiero COUNT(*)hacerlo COUNT(id).

Arth
fuente
0

.... group by to_char(date, 'YYYY') -> 1989

.... group by to_char(date,'MM') -> 05

.... group by to_char(date,'DD') ---> 23

.... group by to_char(date,'MON') ---> MAYO

.... group by to_char(date,'YY') ---> 89

aromita sen
fuente
Esto sería muy muy lento.
earl3s