2016-03-02 40 views
3

我有一張名爲userActivity的表格,其中記錄了每個活動期間。如何在MySQL中計算明智的可用時間?

這裏的表結構:

表:userActivity

ID  user_id  start_time end_time 

當用戶進入在線start time被記錄下來,只要在線狀態改變時,end time被記錄在相應的行。

現在我要生成一個報告,顯示用戶每天的可用時間。

樣品輸入:

ID  user_id   start_time    end_time 

'1'  '1'  '2016-02-28 10:00:00' '2016-02-28 19:00:00' 
'2'  '1'  '2016-02-28 22:00:00' '2016-02-29 10:00:00' 
'3'  '1'  '2016-03-02 10:00:00' '2016-03-02 19:00:00' 
'4'  '1'  '2016-03-02 22:00:00' '2016-03-06 19:00:00' 

預期輸出:

Date   AvailableTime(Hours) 
2016-02-28   11 
2016-02-29   10 
2016-03-02   11 
2016-03-03   24 
2016-03-04   24 
2016-03-05   24 
2016-03-06   19 

到目前爲止,我已經試過:

SELECT 
DATE_FORMAT(start_time,"%Y-%m-%d") `date`, 
TIMESTAMPDIFF(HOUR,start_time,end_time) availableTime 
FROM useractivity 
GROUP BY `date` 

得到輸出:

Date   availableTime(Hours) 

2016-02-28  9 
2016-03-02  9 

這裏的SQL FIDDLE

注: 請忽略user_id暫且。我可以在應用程序級別解決它,但我想在MySQL中處理它。

的時間間隔就可以開始一天,並結束超過一天後

總之,的可用時間是剛好在天軸上的投影(從開始時間和結束時間)。如果開始時間沒有投影到結束時間的同一天,則開始時間將被視爲結束時間投影到的特定日期的開始時間。

描繪圖:

enter image description here

所以可用的時間會從這張截圖如下計算:

28 Feb = (t2-t1) + (t4- t3) 

29 Feb = (t5 - t4) 

02 Mar = (t7 - t6) 
+0

你得到的印象分增加一個小提琴,但它不會使你的問題更清楚。 –

+0

時間間隔是否可以在一天後開始並在一天後結束? (即開始16:th,結束22:nd? –

+0

是的這種情況是可能的。@JoachimIsaksson –

回答

0

我創建的UNION ALL

SELECT sub_query.`date`, SUM(sub_query.available_time) FROM (
    SELECT 
     DATE_FORMAT(start_time,"%Y-%m-%d") `date`, 
     IF(TIMESTAMPDIFF(day,date(start_time),date(end_time))= 0, 
      TIMESTAMPDIFF(HOUR,start_time,end_time),0) AS available_time 
    FROM useractivity 

    UNION ALL 

    SELECT 
     DATE_FORMAT(start_time,"%Y-%m-%d") `date`, 
     IF(TIMESTAMPDIFF(day,date(start_time),date(end_time)) > 0, 
      TIMESTAMPDIFF(HOUR,start_time, date_add(date(start_time),interval 24 hour)),0) AS available_time 
    FROM useractivity 

    UNION ALL 

    SELECT 
     DATE_FORMAT(end_time,"%Y-%m-%d") `date`, 
     IF(TIMESTAMPDIFF(day,date(start_time),date(end_time)) > 0, 
      TIMESTAMPDIFF(HOUR,date(end_time), end_time) , 0) AS available_time 
    FROM useractivity 
) AS sub_query 
GROUP BY sub_query.`date` 

UNION 

SELECT SELECTed_date `date`, 24 FROM 
(SELECT adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) SELECTed_date FROM 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4) v 
WHERE SELECTed_date between (SELECT min(date(start_time)) FROM useractivity) and (SELECT max(date(end_time)) FROM useractivity) 
AND SELECTed_date NOT IN(
SELECT miss_date FROM (
    SELECT date(start_time) AS miss_date FROM useractivity 
    UNION 
    SELECT date(end_time) AS miss_date FROM useractivity 
) AS miss 
) ORDER BY `date`; 
查詢幫助

SQLFiddle

+0

感謝您的回答,但是我想要一個符合要求的解決方案,請參閱我在文章中附上的截圖。@VipinJain –

+0

請運行查詢另一個插入語句'INSERT INTO'useractivity' VALUES('4','1','2016-03-02 22:00:00','2016-03-06 19:00:00');' –

+0

根據我的quey在2016-03-02的輸出是83.應該預期的結果是什麼? –

1

您可以使用日期表進行交叉連接,以在要從日誌時間分割的那一天獲取真正的開始和結束時間。

CREATE TABLE `dates` (
    `date` date , 
    `start_time` timestamp , 
    `end_time` timestamp 
); 

INSERT INTO `dates` VALUES('20160228','2016-02-28 00:00:00', '2016-02-29 00:00:00'); 
INSERT INTO `dates` VALUES('20160229','2016-02-29 00:00:00', '2016-03-01 00:00:00'); 
INSERT INTO `dates` VALUES('20160301','2016-03-01 00:00:00', '2016-03-02 00:00:00'); 
INSERT INTO `dates` VALUES('20160302','2016-03-02 00:00:00', '2016-03-03 00:00:00'); 
INSERT INTO `dates` VALUES('20160303','2016-03-03 00:00:00', '2016-03-04 00:00:00'); 
INSERT INTO `dates` VALUES('20160304','2016-03-04 00:00:00', '2016-03-05 00:00:00'); 
INSERT INTO `dates` VALUES('20160305','2016-03-05 00:00:00', '2016-03-06 00:00:00'); 
INSERT INTO `dates` VALUES('20160306','2016-03-06 00:00:00', '2016-03-07 00:00:00'); 

SELECT 
    u.*, 
    d.date, 
    case when u.start_time<= d.start_time then d.start_time 
     else u.start_time end as `start_time_in_the_day`, 
    case when u.end_time> d.end_time then d.end_time 
     else u.end_time end as `end_time_in_the_day` 
FROM useractivity u 
INNER JOIN dates d 
ON u.start_time< d.end_time 
    and u.end_time>= d.start_time 

然後你只需要總結end_time_in_the_daystart_time_in_the_day之間的時間。

SELECT 
    user_id, 
    date, 
    sum(TIMESTAMPDIFF(HOUR,start_time_in_the_day,end_time_in_the_day)) as `availableTime` 
FROM(
    SELECT 
     u.*, 
     d.date, 
     case when u.start_time<= d.start_time then d.start_time 
      else u.start_time end as `start_time_in_the_day`, 
     case when u.end_time> d.end_time then d.end_time 
      else u.end_time end as `end_time_in_the_day` 
    FROM useractivity u 
    INNER JOIN dates d 
    ON u.start_time< d.end_time 
     and u.end_time>= d.start_time) as t 
group by user_id,date 

SqlFiddle here.

而且我覺得應用TIMESTAMPDIFF(SECOND...代替TIMESTAMPDIFF(HOUR...會更好。

+0

非常感謝。這是另一種方式,但我認爲Vipin的答案更具可擴展性。 +1 –

+0

實際上我會用'MILLISECOND'單位計算可用時間。爲了可讀性,我添加了'HOUR'單位。 –

+0

@AnonymousOne Vipin的回答也有效。但我認爲使用數據模型來講述故事要比使用複雜算法好得多。 –

0

我修改@Vipin耆那教的查詢來實現這個要求:

SELECT sub_query.`date`, SUM(sub_query.available_time) FROM (
    SELECT 
     DATE_FORMAT(start_time,"%Y-%m-%d") `date`, 
     IF(TIMESTAMPDIFF(day,date(start_time),date(end_time))= 0, 
      TIMESTAMPDIFF(HOUR,start_time,end_time),0) AS available_time 
    FROM useractivity 

    UNION ALL 

    SELECT 
     DATE_FORMAT(start_time,"%Y-%m-%d") `date`, 
     IF(TIMESTAMPDIFF(day,date(start_time),date(end_time)) > 0, 
      TIMESTAMPDIFF(HOUR,start_time, date_add(date(start_time),interval 24 hour)),0) AS available_time 
    FROM useractivity 

    UNION ALL 

    SELECT 
     DATE_FORMAT(end_time,"%Y-%m-%d") `date`, 
     IF(TIMESTAMPDIFF(day,date(start_time),date(end_time)) > 0, 
      TIMESTAMPDIFF(HOUR,date(end_time), end_time) , 0) AS available_time 
    FROM useractivity 
) AS sub_query 
GROUP BY sub_query.`date` 

UNION 

SELECT SELECTed_date `date`, 24 FROM 
(SELECT adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) SELECTed_date FROM 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3, 
(SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4) v 
WHERE SELECTed_date between (SELECT min(date(start_time)) FROM useractivity) and (SELECT max(date(end_time)) FROM useractivity) 
AND SELECTed_date NOT IN(
SELECT miss_date FROM (
    SELECT date(start_time) AS miss_date FROM useractivity 
    UNION 
    SELECT date(end_time) AS miss_date FROM useractivity 
) AS miss 
) 
AND EXISTS (SELECT 1 FROM useractivity WHERE SELECTed_date BETWEEN start_time AND end_time) 

ORDER BY `date`;