2011-03-14 95 views
2

假設我有一個包含流媒體連接信息的表。在這張表中,我有一個開始時間和結束時間,當連接開始時,然後關閉。查詢一天內兩次之間的時間

表:日誌

  • ID(INT,PK,AUTO_INCREMENT)
  • 開始時間(DATETIME)
  • 結束時間(DATETIME)

我希望能夠運行一個查詢這將增加一天中建立連接的總時間。這是在一天之內連接明顯:

SELECT 
    SUM(
     TIME_TO_SEC(
      TIMEDIFF(`EndTime`, `StartTime`) 
     ) 
    ) 
WHERE (`StartTime` BETWEEN '2010-01-01' AND '2010-01-02); 

但是,假設一個StartTime開始有一天,各地11:00 PM說了,EndTime是一段時間的第二天,也許凌晨3:00。在這些情況下,我只想分配當天發生的那一段時間。所以,1小時將走向第一天,3小時將走向下一個。

SUM(
    TIME_TO_SEC(
     TIMEDIFF(
      IF(`EndTime`>DATE_ADD('2010-01-01', INTERVAL 1 DAY), DATE_ADD('2010-01-01', INTERVAL 1 DAY), `EndTime`), 
      IF(`StartTime`<'2010-01-01', '2010-01-01', `StartTime`) 
     ) 
    )/60/60 
) 

這樣做的想法是,如果EndTime比一天結束的時候多,那麼我們就用這一天的結束,而不是。如果StartTime小於一天的開始時間,那麼我們將僅使用一天的開始。

所以,我然後需要將所有達包裝成的東西,將產生一個表,看起來像這樣的:

date, total 
2010-01-01, 0 
2010-01-02, 1.53 
2010-01-03, 5.33 

我想這個查詢會工作:

SELECT 
`date`, 
SUM(
    TIME_TO_SEC(
     TIMEDIFF(
      IF(`EndTime`>DATE_ADD(`date`, INTERVAL 1 DAY), DATE_ADD(`date`, INTERVAL 1 DAY), `EndTime`), 
      IF(`StartTime`<`date`, `date`, `StartTime`) 
     ) 
    )/60/60 
) AS `total_hours` 
FROM 
(SELECT * FROM `logs` WHERE `StartTime` BETWEEN '2010-08-01' AND '2010-08-31') AS logs_small, 
(SELECT DATE_ADD("2010-08-01", INTERVAL `number` DAY) AS `date` FROM `numbers` WHERE `number` BETWEEN 0 AND 30) AS `dates` 
GROUP BY `date`; 

注意numbers引用的表是隻有一列的表,number,帶有一系列整數,0,1,2,3等。我在這裏使用它來生成一系列日期,這很好地工作。

這個查詢的問題是我得到不準確的數據。具體來說,logs表中有一個EndDate進入第二天的行在第二天不會計入任何時間。例如,如果我有一排開始於2010-08-01 23:00:00並於2010-08-02 01:00:00結束,則2010-08-02產生的行將加起來爲0。

有沒有更好的方法來做到這一點?理想情況下,我希望在沒有任何記錄與其匹配的日期獲得0而不是null

編輯:爲了澄清,我希望把這個:

id, StartTime, EndTime 
0, 2000-01-01 01:00:00, 2000-01-01 04:00:00 
1, 2000-01-01 23:00:00, 2000-01-02 05:00:00 
2, 2000-01-02 00:00:00, 2000-01-04 01:00:00 

...這個:

date, total_hours 
2000-01-01, 4 
2000-01-02, 29 
2000-01-03, 24 
2000-01-04, 1 
2000-01-05, 0 

解決方案

感謝jim31415提出解決方案!我翻譯了他的回答了在MySQL中可使用的功能以及與此想出了:

SELECT `d`.`Date`, 
     SUM(COALESCE( 
     (CASE WHEN t.StartTime >= d.Date AND t.EndTime < DATE_ADD(d.Date, INTERVAL 1 DAY) THEN TIME_TO_SEC(TIMEDIFF(t.EndTime, t.StartTime)) 
       WHEN t.StartTime < d.Date AND t.EndTime <= DATE_ADD(d.Date, INTERVAL 1 DAY) THEN TIME_TO_SEC(TIMEDIFF(t.EndTime,d.Date)) 
       WHEN t.StartTime >= d.Date AND t.EndTime > DATE_ADD(d.Date, INTERVAL 1 DAY) THEN TIME_TO_SEC(TIMEDIFF(DATE_ADD(d.Date, INTERVAL 1 DAY),t.StartTime)) 
       WHEN t.StartTime < d.Date AND t.EndTime > DATE_ADD(d.Date, INTERVAL 1 DAY) THEN 24*60*60 

     END), 0) 
     )/60/60 ConnectionTime 
FROM (SELECT DATE_ADD('2011-03-01', INTERVAL `number` DAY) AS `Date` FROM `numbers` WHERE `number` BETWEEN 0 AND 30) AS d 
LEFT JOIN `logs` t ON (t.StartTime >= d.Date AND t.StartTime < DATE_ADD(d.Date, INTERVAL 1 DAY)) 
         OR (t.EndTime >= d.Date AND t.EndTime < DATE_ADD(d.Date, INTERVAL 1 DAY)) 
         OR (t.StartTime < d.Date AND t.EndTime > DATE_ADD(d.Date, INTERVAL 1 DAY)) 
GROUP BY d.Date 
ORDER BY d.Date; 

我也應該注意,EndTime空值並不適用於我的情況,因爲我是從舊的日誌文件閱讀在我的應用程序。如果你需要他們,吉姆的帖子讓他們有相當好的概述。

回答

2

這是在MS SQL中,但我認爲這個邏輯適用並可以被翻譯成MySQL。 我不知道你是如何處理EndTime的null,所以我評論說。

select d.Date, 
     sum(coalesce( 
     (case when t.StartTime >= d.Date and t.EndTime < dateadd(day,1,d.Date) then datediff(minute,t.StartTime,t.EndTime) 
       when t.StartTime < d.Date and t.EndTime <= dateadd(day,1,d.Date) then datediff(minute,d.Date,t.EndTime) 
       when t.StartTime >= d.Date and t.EndTime > dateadd(day,1,d.Date) then datediff(minute,t.StartTime,dateadd(day,1,d.Date)) 
       when t.StartTime < d.Date and t.EndTime > dateadd(day,1,d.Date) then 24*60 
       --when t.StartTime >= d.Date and t.EndTime is null then datediff(minute,t.StartTime,getdate()) 
       --when t.StartTime < d.Date and t.EndTime is null then datediff(minute,d.Date,getdate()) 
     end), 0) 
     ) ConnectionTime 
from (select Date=dateadd(day, num, '2011-03-01') from #NUMBERS where num between 0 and 30) d 
left join Logs t on (t.StartTime >= d.Date and t.StartTime < dateadd(day,1,d.Date)) 
         or (t.EndTime >= d.Date and t.EndTime < dateadd(day,1,d.Date)) 
         or (t.StartTime < d.Date and t.EndTime > dateadd(day,1,d.Date)) 
group by d.Date 
order by d.Date 
+0

工作很好!謝謝!我將發佈我用於MySQL版本的內容作爲對我的文章的修改。 – Brad 2011-03-14 23:56:56

1

使用工會,使其更容易爲自己

SELECT 
`date`, 
SUM(
    TIME_TO_SEC(TIMEDIFF(`EndTime`,`StartTime`))/60/60 
) AS `total_hours` 
FROM 

(SELECT id, starttime, if (endtime > date then date else endtime) FROM `logs` WHERE `StartTime` >= date AND `StartTime` < date 
union all 
SELECT id, date, endtime FROM `logs` WHERE `enddate` >= date AND `enddate` < date and !(`StartTime` >= date AND `StartTime` < date) 
union all 
SELECT id, date, date_add(date, 1) FROM `logs` WHERE `enddate` > date AND `startdate` < date 
) as datedetails inner join 
    (SELECT DATE_ADD("2010-08-01", INTERVAL `number` DAY) AS `date` FROM `numbers` WHERE `number` BETWEEN 0 AND 30) AS `dates` 
GROUP BY `date`; 

希望,我明白你的問題正確

編輯:忘記,當有這一天要求之前開始多日的請求的情況下,和結束後

+0

我得到「未知列‘日期’」,我相信這是因爲你在一個正在被聯合的選擇使用'date'列,只從'logs'中選擇。 '日期'列來自動態生成的'日期'表,所以我不認爲我可以選擇這種方式。或者我誤解了? – Brad 2011-03-14 04:00:21

+0

我在帖子底部添加了一個編輯,以說明我在找什麼。對不起,如果它還有點不清楚。我在解決問題時遇到了麻煩。 – Brad 2011-03-14 04:04:25

+0

我對語法有點懶,只是爲了讓你知道如何解決問題。第一個選擇是獲取開始日期是您選擇的那一天的小事。第二個是它不在的地方,但是結束的時候是,第三個是日誌在你要求的那一天的周圍 – Cine 2011-03-14 04:09:51

1

使用此

select startTime,duration as duration,time,TIME_TO_SEC(TIMEDIFF(time,startTime)) as diff from <idling> limit 25; 

select startTime,duration DIV 60 as duration,time,TIMESTAMPDIFF(MINUTE,startTime,time) as diff from <idling> limit 25;