Grupo MySQL por horas

Estoy tratando de obtener un informe de mi tabla de historial por uso por hora. history la mesa es;

CREATE TABLE IF NOT EXISTS `history` (
`history_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned NOT NULL DEFAULT '0',
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`history_id`),
KEY `user_id` (`user_id`),
KEY `created` (`created`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

Quiero agrupar por HOUR y COUNT todos los registros en un intervalo de fechas determinado.

Hours            |    Usage
------------------------------------
00:00-01:00      |      5
01:00-02:00      |      9
02:00-03:00      |      0 (or NULL)
03:00-04:00      |      20
...
...
...
22:00-23:00      |      11
23:00-00:00      |      1

Utilicé una consulta como esta, pero no muestra todas las horas.

SELECT 

CASE 
  WHEN HOUR(created) BETWEEN 0 AND 1 THEN '00:00 - 01:00'
  WHEN HOUR(created) BETWEEN 1 AND 2 THEN '01:00 - 02:00'
  WHEN HOUR(created) BETWEEN 2 AND 3 THEN '02:00 - 03:00'
  WHEN HOUR(created) BETWEEN 3 AND 4 THEN '03:00 - 04:00'
  WHEN HOUR(created) BETWEEN 4 AND 5 THEN '04:00 - 05:00'
  WHEN HOUR(created) BETWEEN 5 AND 6 THEN '05:00 - 06:00'
  WHEN HOUR(created) BETWEEN 6 AND 7 THEN '06:00 - 07:00'
  WHEN HOUR(created) BETWEEN 7 AND 8 THEN '07:00 - 08:00'
  WHEN HOUR(created) BETWEEN 8 AND 9 THEN '08:00 - 09:00'
  WHEN HOUR(created) BETWEEN 9 AND 10 THEN '09:00 - 10:00'
  WHEN HOUR(created) BETWEEN 10 AND 11 THEN '10:00 - 11:00'
  WHEN HOUR(created) BETWEEN 11 AND 12 THEN '11:00 - 12:00'
  WHEN HOUR(created) BETWEEN 12 AND 13 THEN '12:00 - 13:00'
  WHEN HOUR(created) BETWEEN 13 AND 14 THEN '13:00 - 14:00'
  WHEN HOUR(created) BETWEEN 14 AND 15 THEN '14:00 - 15:00'
  WHEN HOUR(created) BETWEEN 15 AND 16 THEN '15:00 - 16:00'
  WHEN HOUR(created) BETWEEN 16 AND 17 THEN '16:00 - 17:00'
  WHEN HOUR(created) BETWEEN 17 AND 18 THEN '17:00 - 18:00'
  WHEN HOUR(created) BETWEEN 18 AND 19 THEN '18:00 - 19:00'
  WHEN HOUR(created) BETWEEN 19 AND 20 THEN '19:00 - 20:00'
  WHEN HOUR(created) BETWEEN 20 AND 21 THEN '20:00 - 21:00'
  WHEN HOUR(created) BETWEEN 21 AND 22 THEN '21:00 - 23:00'
  WHEN HOUR(created) BETWEEN 22 AND 23 THEN '22:00 - 23:00'
  WHEN HOUR(created) BETWEEN 23 AND 24 THEN '23:00 - 00:00'
END AS `Hours`,

COUNT(*) AS `usage`
FROM history
WHERE (created BETWEEN '2012-02-07' AND NOW())
GROUP BY 
  CASE 
    WHEN HOUR(created) BETWEEN 0 AND 1 THEN 1
    WHEN HOUR(created) BETWEEN 1 AND 2 THEN 2
    WHEN HOUR(created) BETWEEN 2 AND 3 THEN 3
    WHEN HOUR(created) BETWEEN 3 AND 4 THEN 4
    WHEN HOUR(created) BETWEEN 4 AND 5 THEN 5
    WHEN HOUR(created) BETWEEN 5 AND 6 THEN 6
    WHEN HOUR(created) BETWEEN 6 AND 7 THEN 7
    WHEN HOUR(created) BETWEEN 7 AND 8 THEN 8
    WHEN HOUR(created) BETWEEN 8 AND 9 THEN 9
    WHEN HOUR(created) BETWEEN 9 AND 10 THEN 10
    WHEN HOUR(created) BETWEEN 10 AND 11 THEN 11
    WHEN HOUR(created) BETWEEN 11 AND 12 THEN 12
    WHEN HOUR(created) BETWEEN 12 AND 13 THEN 13
    WHEN HOUR(created) BETWEEN 13 AND 14 THEN 14
    WHEN HOUR(created) BETWEEN 14 AND 15 THEN 15
    WHEN HOUR(created) BETWEEN 15 AND 16 THEN 16
    WHEN HOUR(created) BETWEEN 16 AND 17 THEN 17
    WHEN HOUR(created) BETWEEN 17 AND 18 THEN 18
    WHEN HOUR(created) BETWEEN 18 AND 19 THEN 19
    WHEN HOUR(created) BETWEEN 19 AND 20 THEN 20
    WHEN HOUR(created) BETWEEN 20 AND 21 THEN 21
    WHEN HOUR(created) BETWEEN 21 AND 22 THEN 22
    WHEN HOUR(created) BETWEEN 22 AND 23 THEN 23
    WHEN HOUR(created) BETWEEN 23 AND 24 THEN 24
END

Solo se muestra si hay registros.

Hours            |    Usage
------------------------------------
00:00-01:00      |      5
01:00-02:00      |      9
23:00-00:00      |      1

preguntado el 03 de mayo de 12 a las 11:05

Cree una tabla temporal con todas las horas y únase a la izquierda con sus datos reales. -

4 Respuestas

Su consulta existente se puede reducir a:

SELECT   CONCAT(HOUR(created), ':00-', HOUR(created)+1, ':00') AS Hours
  ,      COUNT(*) AS `usage`
FROM     history
WHERE    created BETWEEN '2012-02-07' AND NOW()
GROUP BY HOUR(created)

Para mostrar cada hora, incluidas aquellas para las que no hay datos, debe realizar una combinación externa con una tabla que contenga todas las horas para las que desea datos. Puede construir una tabla de este tipo en su consulta usando UNION:

SELECT   CONCAT(Hour, ':00-', Hour+1, ':00') AS Hours
  ,      COUNT(created) AS `usage`
FROM     history
  RIGHT JOIN (
                   SELECT  0 AS Hour
         UNION ALL SELECT  1 UNION ALL SELECT  2 UNION ALL SELECT  3
         UNION ALL SELECT  4 UNION ALL SELECT  5 UNION ALL SELECT  6
         UNION ALL SELECT  7 UNION ALL SELECT  8 UNION ALL SELECT  9
         UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
         UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
         UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
         UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
         UNION ALL SELECT 22 UNION ALL SELECT 23
  )      AS AllHours ON HOUR(created) = Hour
WHERE    created BETWEEN '2012-02-07' AND NOW() OR created IS NULL
GROUP BY Hour
ORDER BY Hour

Sin embargo, el tratamiento de grupos para los que no existen datos es realmente una cuestión de lógica comercial que está mejor ubicada en su capa de acceso a datos en lugar de en la base de datos misma: de hecho, debería ser trivial para su aplicación usar un valor cero cada vez que una hora es ausente.

Respondido el 19 de Septiembre de 15 a las 12:09

Dada una mesa Log con columnas ts y propuesta de, lo siguiente dará el promedio por hora de las últimas 24 horas (suponiendo que cada hora tenga al menos una fila).

SELECT 
    FROM_UNIXTIME( TRUNCATE( UNIX_TIMESTAMP( `ts` )/3600, 0 ) * 3600 ) as h,
    AVG( `value` ) as v,
    COUNT( * ) as q
FROM 
    Log
GROUP BY 
   h
ORDER BY 
   h desc
LIMIT
    24

La columna ts puede ser una columna de marca de tiempo o fecha y hora mientras propuesta de es cualquier cosa que AVG() aceptará.

Respondido el 06 de enero de 16 a las 10:01

Modificando la respuesta de @eggyal, ya que otro buen caso de uso sería mostrar el día junto con las horas.

Suponga que necesita COUNT de registros de los últimos 7 días, que es de 24 horas cada uno.

SELECT
   dayname(date_sub(curdate(), INTERVAL 1 DAY)) AS Day,
   CONCAT(Hour, ':00-', Hour+1, ':00') AS Hour,
   COUNT(created) AS `usage`
FROM
   history
   RIGHT JOIN
      (
         SELECT  0 AS Hour
         UNION ALL SELECT  1 UNION ALL SELECT  2 UNION ALL SELECT  3
         UNION ALL SELECT  4 UNION ALL SELECT  5 UNION ALL SELECT  6
         UNION ALL SELECT  7 UNION ALL SELECT  8 UNION ALL SELECT  9
         UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
         UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
         UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
         UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
         UNION ALL SELECT 22 UNION ALL SELECT 23
      )
      AS totalhours
      ON HOUR(created) = HOUR
      AND DATE(created) = date_sub(curdate(), INTERVAL 1 DAY)
      OR created IS NULL
GROUP BY
   Hour
ORDER BY
   Hour;

Resultado SQL

Modificando esto en la lógica de negocios del código backend o en un procedimiento almacenado,

INTERVAL 1 DAY

puede generar resultados de consultas de los últimos 7 días junto con el nombre del día.

Respondido el 29 de Septiembre de 20 a las 13:09

Sobre la base de la respuesta de Prashanth, recibí una advertencia sobre la ambigüedad de Hour (Percona 5.7). Además, no estaba obteniendo los resultados en el orden correcto. Lo siguiente resolvió estos problemas para mí y es más claro con respecto a las cláusulas GROUP BY y ORDER BY:

SELECT
  dayname(date_sub(curdate(), INTERVAL 1 DAY)) AS Day,
  CONCAT(Hour, ':00-', Hour+1, ':00') AS Hour,
  COUNT(created_at) AS `total`
FROM logtable
RIGHT JOIN
  (
     SELECT  0 AS Hour
     UNION ALL SELECT  1 UNION ALL SELECT  2 UNION ALL SELECT  3
     UNION ALL SELECT  4 UNION ALL SELECT  5 UNION ALL SELECT  6
     UNION ALL SELECT  7 UNION ALL SELECT  8 UNION ALL SELECT  9
     UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
     UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
     UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
     UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
     UNION ALL SELECT 22 UNION ALL SELECT 23
  )
  AS totalhours
  ON ( HOUR(created_at) = HOUR
  AND DATE(created_at) = date_sub(curdate(), INTERVAL 1 DAY) )
  OR created_at IS NULL
GROUP BY
   CONCAT(Hour, ':00-', Hour+1, ':00')
ORDER BY
   totalhours.Hour;

Respondido el 30 de junio de 21 a las 22:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.