2010-06-21 4 views
32

J'ai une table (MySQL) qui capture des échantillons toutes les n secondes. La table a beaucoup de colonnes, mais tout ce qui compte pour cela est deux: un horodatage (de type TIMESTAMP) et un nombre (de type INT).SELECT/GROUP BY - segments de temps (10 secondes, 30 secondes, etc.)

Ce que je voudrais faire, c'est obtenir des sommes et des moyennes de la colonne de compte sur une série de fois. Par exemple, j'ai des échantillons toutes les 2 secondes enregistrées, mais je voudrais la somme de la colonne de compte pour tous les échantillons dans une fenêtre de 10 secondes ou 30 secondes pour tous les échantillons.

Voici un exemple des données:

 
+---------------------+-----------------+ 
| time_stamp   | count   | 
+---------------------+-----------------+ 
| 2010-06-15 23:35:28 |    1 | 
| 2010-06-15 23:35:30 |    1 | 
| 2010-06-15 23:35:30 |    1 | 
| 2010-06-15 23:35:30 |    942 | 
| 2010-06-15 23:35:30 |    180 | 
| 2010-06-15 23:35:30 |    4 | 
| 2010-06-15 23:35:30 |    52 | 
| 2010-06-15 23:35:30 |    12 | 
| 2010-06-15 23:35:30 |    1 | 
| 2010-06-15 23:35:30 |    1 | 
| 2010-06-15 23:35:33 |   1468 | 
| 2010-06-15 23:35:33 |    247 | 
| 2010-06-15 23:35:33 |    1 | 
| 2010-06-15 23:35:33 |    81 | 
| 2010-06-15 23:35:33 |    16 | 
| 2010-06-15 23:35:35 |   1828 | 
| 2010-06-15 23:35:35 |    214 | 
| 2010-06-15 23:35:35 |    75 | 
| 2010-06-15 23:35:35 |    8 | 
| 2010-06-15 23:35:37 |   1799 | 
| 2010-06-15 23:35:37 |    24 | 
| 2010-06-15 23:35:37 |    11 | 
| 2010-06-15 23:35:37 |    2 | 
| 2010-06-15 23:35:40 |    575 | 
| 2010-06-15 23:35:40 |    1 | 
| 2010-06-17 10:39:35 |    2 | 
| 2010-06-17 10:39:35 |    2 | 
| 2010-06-17 10:39:35 |    1 | 
| 2010-06-17 10:39:35 |    2 | 
| 2010-06-17 10:39:35 |    1 | 
| 2010-06-17 10:39:40 |    35 | 
| 2010-06-17 10:39:40 |    19 | 
| 2010-06-17 10:39:40 |    37 | 
| 2010-06-17 10:39:42 |    64 | 
| 2010-06-17 10:39:42 |    3 | 
| 2010-06-17 10:39:42 |    31 | 
| 2010-06-17 10:39:42 |    7 | 
| 2010-06-17 10:39:42 |    246 | 
+---------------------+-----------------+ 

La sortie Je voudrais (sur la base des données ci-dessus) devrait ressembler à ceci:

 
+---------------------+-----------------+ 
| 2010-06-15 23:35:00 |    1 | # This is the sum for the 00 - 30 seconds range 
| 2010-06-15 23:35:30 |   7544 | # This is the sum for the 30 - 60 seconds range 
| 2010-06-17 10:39:35 |    450 | # This is the sum for the 30 - 60 seconds range 
+---------------------+-----------------+ 

J'ai utilisé GROUP BY pour recueillir ces nombres par seconde, ou par minute, mais je n'arrive pas à comprendre la syntaxe pour que les commandes GROUP BY de sous-minute ou de plage de secondes fonctionnent correctement.

Je vais principalement utiliser cette requête pour siphonner les données de cette table vers une autre table.

Merci!

Répondre

58

GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 30

ou dire pour une raison que vous vouliez les regrouper en 20 secondes, il serait DIV 20 etc. Pour modifier les limites entre GROUP BY valeurs que vous pouvez utiliser

GROUP BY (UNIX_TIMESTAMP(time_stamp) + r) DIV 30

r est un entier non négatif littéral inférieur à 30. So

GROUP BY (UNIX_TIMESTAMP(time_stamp) + 5) DIV 30

devrait vous donner des sommes entre hh: mm: 05 et hh: mm: 35 et entre hh: mm: 35 et hh: mm + 1: 05.

+0

Parfait! Cela faisait * exactement * ce dont j'avais besoin! Merci beaucoup! –

6

J'ai essayé la solution Hammerite dans mon projet, mais elle ne fonctionnait pas bien lorsqu'il y avait des échantillons manquants de la série. Voici un exemple de la requête qui est censé sélectionner l'horodatage (ts), le nom d'utilisateur et mesure moyenne de metric_table et regrouper les résultats par des intervalles de temps de 27 minutes:

select 
    min(ts), 
    user_name, 
    sum(measure)/27 
from metric_table 
where 
    ts between date_sub('2015-03-17 00:00:00', INTERVAL 2160 MINUTE) and '2015-03-17 00:00:00' 

group by unix_timestamp(ts) div 1620, user_name 
order by ts, user_name 
; 

Note: 27 minutes (en sélection) = 1620 secondes (en groupe par), 2160 minutes = 3 jours (c'est la plage horaire)

Lorsque j'ai exécuté cette requête sur une série chronologique où des échantillons ont été enregistrés de manière irrégulière (en d'autres termes: pour un horodatage donné, il n'y avait pas de garantie de trouver des valeurs de mesure pour tous les noms d'utilisateur) les résultats n'ont pas été estampillés selon l'intervalle (n'ont pas été placés toutes les 27 minutes). Je soupçonne que cela était dû au fait que min (ts) renvoyait un horodatage dans certains groupes qui était supérieur au plancher attendu (intervalle ts0 + i *). J'ai modifié l'ancienne requête à celui-ci:

select 
    from_unixtime(unix_timestamp(ts) - unix_timestamp(ts) mod 1620) as ts1, 
    user_name, 
    sum(measure)/27 
from metric_table 
where 
    ts between date_sub('2015-03-17 00:00:00', INTERVAL 2160 MINUTE) and '2015-03-17 00:00:00' 

group by ts1, user_name 
order by ts1, user_name 
; 

et cela fonctionne bien même lorsque les échantillons sont manquants. Je pense que c'est parce qu'une fois que les mathématiques sont déplacées pour les sélectionner, cela garantit que ts1 s'alignera sur les pas de temps.

+0

Merci d'avoir soulevé cette question, m'a beaucoup aidé! – citysurrounded

+0

Des trucs merveilleux! Tout ce dont j'ai besoin maintenant est un moyen pour qu'il enregistre une ligne "zéro" quand il n'y a pas d'échantillons dans ce seau de temps ... –

+0

@DanielRhodes est-ce que c'est déjà ça? –

0

Très étrange, mais en utilisant la solution ici:

Average of data for every 5 minutes in the given times

Nous pouvons vous proposer quelque chose comme:

select convert(
(min(dt_record) div 50)*50 - 20*((convert(min(dt_record), datetime) div 50) mod 2), 

datetime) comme dt, avg (1das4hrz) de meteor-m2_msgi où dt_record> = '2016-11-13 05:00:00' et dt_record < '2016-11-14 00:00:00' groupe par convertir (dt_record, datetime) div 50;

select (
convert(
min(dt_record), datetime) div 50)*50 - 20*(
(convert(min(dt_record), datetime) div 50) mod 2 
) as dt, 
avg(column) from `your_table` 
where dt_record>='2016-11-13 05:00:00' 
and dt_record < '2016-11-14 00:00:00' 
group by convert(dt_record, datetime) div 50; 

50 est parce que 1/2 de NORMAL minutes a 30 secondes pendant Integer 'FORMAT DATE' nous supposons que de diviser par 50

2

Une autre solution.

Pour moyenne sur tout intervalle que vous aimez, vous pouvez convertir votre dt en horodatage et de regrouper par modulo par votre intervalle (7 secondes dans l'exemple).

select FROM_UNIXTIME(
    UNIX_TIMESTAMP(dt_record) - UNIX_TIMESTAMP(dt_record) mod 7 
) as dt, avg(1das4hrz) from `meteor-m2_msgi` 
where dt_record>='2016-11-13 05:00:00' 
and dt_record < '2016-11-13 05:02:00' 
group by FROM_UNIXTIME(
    UNIX_TIMESTAMP(dt_record) - UNIX_TIMESTAMP(dt_record) mod 7); 

Pour montrer comment cela fonctionne, je prépare une requête, montrant les calculs.

select dt_record, minute(dt_record) as mm, SECOND(dt_record) as ss, 
UNIX_TIMESTAMP(dt_record) as uxt, UNIX_TIMESTAMP(dt_record) mod 7 as ux7, 
FROM_UNIXTIME(
    UNIX_TIMESTAMP(dt_record) - UNIX_TIMESTAMP(dt_record) mod 7) as dtsub, 
column from `yourtable` where dt_record>='2016-11-13 05:00:00' 
and dt_record < '2016-11-13 05:02:00'; 

+---------------------+--------------------+ 
| dt     | avg(column)  | 
+---------------------+--------------------+ 
| 2016-11-13 04:59:43 | 25434.85714285714 | 
| 2016-11-13 05:00:42 | 5700.728813559322 | 
| 2016-11-13 05:01:41 | 950.1016949152543 | 
| 2016-11-13 05:02:40 | 4671.220338983051 | 
| 2016-11-13 05:03:39 | 25468.728813559323 | 
| 2016-11-13 05:04:38 | 43883.52542372881 | 
| 2016-11-13 05:05:37 | 24589.338983050846 | 
+---------------------+--------------------+ 


+---------------------+-----+-----+------------+------+---------------------+----------+ 
| dt_record   | mm | ss | uxt  | ux7 | dtsub    | column | 
+---------------------+------+-----+------------+------+---------------------+----------+ 
| 2016-11-13 05:00:00 | 0 | 0 | 1479002400 | 1 | 2016-11-13 04:59:59 | 36137 | 
| 2016-11-13 05:00:01 | 0 | 1 | 1479002401 | 2 | 2016-11-13 04:59:59 | 36137 | 
| 2016-11-13 05:00:02 | 0 | 2 | 1479002402 | 3 | 2016-11-13 04:59:59 | 36137 | 
| 2016-11-13 05:00:03 | 0 | 3 | 1479002403 | 4 | 2016-11-13 04:59:59 | 34911 |  
| 2016-11-13 05:00:04 | 0 | 4 | 1479002404 | 5 | 2016-11-13 04:59:59 | 34911 | 
| 2016-11-13 05:00:05 | 0 | 5 | 1479002405 | 6 | 2016-11-13 04:59:59 | 34911 | 
| 2016-11-13 05:00:06 | 0 | 6 | 1479002406 | 0 | 2016-11-13 05:00:06 | 33726 | 
| 2016-11-13 05:00:07 | 0 | 7 | 1479002407 | 1 | 2016-11-13 05:00:06 | 32581 | 
| 2016-11-13 05:00:08 | 0 | 8 | 1479002408 | 2 | 2016-11-13 05:00:06 | 32581 | 
| 2016-11-13 05:00:09 | 0 | 9 | 1479002409 | 3 | 2016-11-13 05:00:06 | 31475 | 
+---------------------+-----+-----+------------+------+---------------------+----------+ 

Quelqu'un peut-il suggérer quelque chose de plus rapide?

Questions connexes