2010-11-04 4 views
14

Così ho una serie di risultati che sembra qualcosa di simile:MySQL: Come SUM() un TIMEDIFF() su un gruppo?

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 | 
------------------------------------------------------------------ 

Ora ho bisogno di raggruppare i risultati per User_ID e SUM() TimeDiff. Se aggiungo una clausola GROUP BY, non è SUM() TimeDiff (e non me lo aspetterei). Come posso SUM() TimeDiffs per ciascun utente?

risposta

30

Usa:

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 

Passi:

  1. Utilizzare TIME_TO_SEC per convertire il tempo di secondi per operazione matematica
  2. Somma la differenza
  3. Usa SEC_TO_TIME per convertire i secondi di nuovo a tempo

Sulla base dei dati di esempio, avrei appena suggerito:

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

NOTA: Non c'è un bug in questo codice se si utilizza datetime. TIME_TO_SEC converte solo la sezione temporale in modo che si finiscano con i grandi negativi se l'orologio supera la mezzanotte. Utilizza UNIX_TIMESTAMP invece per fare la somma. Anche SEC_TO_TIME raggiunge valori superiori a 3020399 secondi, ad es. SELEZIONA TIME_TO_SEC (SEC_TO_TIME (3020400)); Se vedi vedi questo valore 838: 59: 59 hai raggiunto il limite massimo e probabilmente hai solo bisogno di dividere per 3600 per mostrare solo le ore.

+0

Dannazione, avrebbe dovuto conosciuto circa 'TIME_TO_SEC' in precedenza, io e la mia kludgy' CONCAT' con date a caso .... +1 per te! – Wrikken

+0

@Wrikken: i timestamp di epoca non funzionavano così bene, quindi ho dovuto improvvisare :) –

+0

scusate, avrei dovuto essere più specifico. Le ore di inizio/fine sono in realtà datate. Questo cambia la tua risposta? – Andrew

1

Funzionerà per voi?

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

+1

per quanto ne so, '' SUM' su valori time' dà risultati brutto ('01:30 + 02: 42' diventa' 0242' 0130 + => '0372' => tempo non valido/NULL. – Wrikken

+1

Wrikken è corretto - Ho ottenuto null come risultato quando ho testato questo in base ai dati di esempio –

+0

+1. Hai ragione ragazzi. Indovina male, l'ho provato e ha funzionato correttamente se la differenza non ha parti minuti/secondi.Ho perso il punto che il tempo può anche avere minuti/secondi :( – a1ex07

2

per quanto ne so, l'unica opzione è quella di lanciare a UNIX_TIMESTAMP s e fare alcuni calcoli interi, sostituendo una data casuale (io ho scelto 2000-01-01) per le colonne Tempo senza una data.

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; 

perché può sembrare puoi SUM colonne del tempo, ma in realtà sarà gettato in interi brutte o galleggianti che non seguiranno le specifiche di tempo (provare con una somma di minuti> 60 e sarete capito quello che intendo).


Per quelli che affermano che puoi SUM colonne di tempo:

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

No, non sarà. Io modificare in un esempio che ha minuti non pari a '0' in esso in un momento. – Wrikken

+1

Sì, il mio male, ho non ho avuto abbastanza tempo per vedere che 1:59 + 1:59 non è uguale a 3:18 :-p. –

+0

Hehe, qui fuori, va bene, potresti credermi che avevo un sistema di gestione stipendi in esecuzione su questa errata ipotesi per diverse settimane molto tempo fa? Arrabbiati mob tutto intorno :) – Wrikken

0

ti suggerisco di usare TO_SECONDS invece:

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