2010-11-04 7 views
14

J'ai un ensemble de résultats qui ressemble à quelque chose comme ceci:MySQL: Comment SUM() un TIMEDIFF() sur un groupe?

SELECT User_ID, StartTime, EndTime, TIMEDIFF(EndTime, StartTime) AS TimeDiff 
FROM MyTable 

------------------------------------------------------------------ 
| User_ID |  StartTime  |   EndTime  | TimeDiff | 
------------------------------------------------------------------ 
| 1 | 2010-11-05 08:00:00 | 2010-11-05 09:00:00 | 01:00:00 | 
------------------------------------------------------------------ 
| 1 | 2010-11-05 09:00:00 | 2010-11-05 10:00:00 | 01:00:00 | 
------------------------------------------------------------------ 
| 2 | 2010-11-05 06:30:00 | 2010-11-05 07:00:00 | 00:30:00 | 
------------------------------------------------------------------ 
| 2 | 2010-11-05 07:00:00 | 2010-11-05 09:00:00 | 02:00:00 | 
------------------------------------------------------------------ 
| 2 | 2010-11-05 09:00:00 | 2010-11-05 10:00:00 | 01:00:00 | 
------------------------------------------------------------------ 

Maintenant je dois grouper les résultats par User_ID et SUM() TIMEDIFF. Si j'ajoute une clause GROUP BY, ce n'est pas SUM() le TimeDiff (et je ne m'y attendrais pas). Comment puis-je SUM() les TimeDiffs pour chaque utilisateur?

Répondre

30

Utilisation:

SELECT t.user_id,  
     SEC_TO_TIME(SUM(TIME_TO_SEC(t.endtime) - TIME_TO_SEC(t.starttime))) AS timediff 
    FROM MYTABLE t 
GROUP BY t.user_id 

Étapes:

  1. Utilisez TIME_TO_SEC pour convertir le temps en secondes pour opération mathématique
  2. Somme la différence
  3. Utilisez SEC_TO_TIME pour convertir les secondes en arrière TIME

Sur la base des données d'échantillon, j'aurais simplement suggéré:

SELECT t.user_id,  
     TIMEDIFF(MIN(t.startdate), MAX(t.enddate)) AS timediff 
    FROM MYTABLE t 
GROUP BY t.user_id 

NOTE: Il est un bogue dans ce code si vous utilisez datetime. TIME_TO_SEC ne convertit la section de temps que si vous obtenez de gros négatifs si l'horloge dépasse minuit. Utilisez UNIX_TIMESTAMP à la place pour faire la somme. SEC_TO_TIME atteint également des valeurs supérieures à 3020399 secondes, par ex. SELECT TIME_TO_SEC (SEC_TO_TIME (3020400)); Si vous voir cette valeur 838: 59: 59 vous avez atteint le maximum et probablement juste besoin de pour diviser par 3600 pour afficher les heures.

+0

Damn, aurait dû savoir' TIME_TO_SEC' plus tôt, moi et mon kludgy ' CONCAT's avec date aléatoire .... +1 pour vous! – Wrikken

+0

@Wrikken: les horodateurs de l'époque ne fonctionnaient pas très bien, donc j'ai dû improviser :) –

+0

désolé, j'aurais dû être plus précis. Les heures de début/fin sont en réalité des datetimes. Cela change-t-il votre réponse? – Andrew

1

Est-ce que cela fonctionnera pour votre?

SELECT User_ID, TIME(SUM(TIMEDIFF(EndTime, StartTime))) AS TimeDiff
FROM MyTable GROUP BY User_ID

+1

AFAIK, '' SUM' sur les valeurs time' donne des résultats désagréables ('01h30 + 02: 42' devient' 0130 + 0242' => '0372' => temps invalide/NULL – Wrikken

+1

Wrikken est correct - J'ai eu le résultat nul lors du test basé sur les données de l'échantillon –

+0

+1 Vous avez raison les gars, je l'ai essayé et ça a marché correctement si la différence n'a pas de parties minute/seconde.J'ai manqué le point que le temps peut aussi avoir des minutes/secondes :( – a1ex07

2

AFAIK, votre seule option est de jeter à UNIX_TIMESTAMP s et faire des calculs entiers, en remplaçant une date aléatoire (j'ai choisi 2000-01-01) pour les colonnes TIME sans date.

SELECT TIMEDIFF(
    DATE_ADD('2000-01-01 00:00:00', 
     INTERVAL 
     SUM(UNIX_TIMESTAMP(CONCAT('2000-01-01 ',TimeDiff)) - UNIX_TIMESTAMP('2000-01-01 00:00:00') 
     SECOND), 
    '2000-01-01 00:00:00') 
FROM MyTable; 

Parce qu'il peut sembler vous pouvez SUM colonnes de temps, mais en fait, ils seront jetés aux entiers désagréables ou flotteurs qui ne suivront pas les spécifications de temps (essayez avec une somme de minutes> 60 et vous tu vois ce que je veux dire).


Pour ceux qui prétendent que vous pouvez SUM colonnes de temps:

mysql> create table timetest(a TIME); 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO timetest VALUES ('02:00'),('03:00'); 
Query OK, 2 rows affected (0.00 sec) 
Records: 2 Duplicates: 0 Warnings: 0 

mysql> SELECT SUM(a) FROM timetest; 
+--------+ 
| SUM(a) | 
+--------+ 
| 50000 | 
+--------+ 
1 row in set (0.00 sec) 

mysql> SELECT TIME(SUM(a)) FROM timetest; 
+--------------+ 
| TIME(SUM(a)) | 
+--------------+ 
| 05:00:00  | 
+--------------+ 
1 row in set (0.00 sec) 

mysql> -- seems ok, but wait 
mysql> INSERT INTO timetest VALUES ('02:30'); 
Query OK, 1 row affected (0.01 sec) 

mysql> SELECT TIME(SUM(a)) FROM timetest; 
+--------------+ 
| TIME(SUM(a)) | 
+--------------+ 
| 07:30:00  | 
+--------------+ 
1 row in set (0.00 sec) 

mysql> -- and now, oh ye unbelievers: 
mysql> INSERT INTO timetest VALUES ('01:40'); 
Query OK, 1 row affected (0.00 sec) 

mysql> SELECT TIME(SUM(a)) FROM timetest; 
+--------------+ 
| TIME(SUM(a)) | 
+--------------+ 
| NULL   | 
+--------------+ 
1 row in set, 1 warning (0.00 sec) 

mysql> -- why is that? because it uses integer arithmetic, not time - arithmetic: 
mysql> SELECT SUM(a) FROM timetest; 
+--------+ 
| SUM(a) | 
+--------+ 
| 87000 | 
+--------+ 
1 row in set (0.00 sec) 

mysql> -- that cannot be cast to time 
+1

Non, je ne vais pas, je vais éditer dans un exemple qui a des minutes ne s'élevant pas à 0 dans un instant – Wrikken

+1

Yep, mon mauvais, je n'a pas pris assez de temps pour voir que 1:59 + 1:59 n'est pas égal à 3:18 :-p –

+0

Hehe, ici, c'est bien, pourriez-vous croire que j'avais un système de paie fonctionnant sur cette supposition incorrecte depuis plusieurs semaines il y a longtemps longtemps? Des foules en colère tout autour :) – Wrikken

0

Je vous suggère d'utiliser à la place TO_SECONDS:

SELECT t.user_id,  
     SEC_TO_TIME(SUM(TO_SECONDS(t.endtime) - TO_SECONDS(t.starttime))) AS timediff 
    FROM MYTABLE t 
GROUP BY t.user_id