2012-11-09 42 views
1

我一直在MySQL表上使用TimeDiff來獲取兩個字段之間的差異,兩者都是DateTime格式。以下是我正在使用的查詢,這也將時間限制在今年。MySQL TimeDiff排除週末

SELECT username, CONCAT(
FLOOR(SUM(HOUR(TIMEDIFF(end_time, start_time))/24)), ' days ', 
MOD(HOUR(TIMEDIFF(end_time, start_time)), 24), ' hours ', 
MINUTE(TIMEDIFF(end_time, start_time)), ' minutes') 
AS duration 
FROM table 
WHERE start_time > CONCAT(YEAR(CURDATE()) -1, '-12-31') 
GROUP BY username 

我遇到的問題是,我一直在努力解決如何從結果中排除週末的問題。任何人都可以幫忙嗎?

+0

有一些偉大的想法[這裏](http://stackoverflow.com/questions/2222563/mysql-calculate-the-difference-between-date-times-only-during-mf-work-week )。 –

+1

定義「從結果中排除週末」。你的意思是從'duration'中減去週末時間,或者排除開始,結束或包含週末的記錄? –

+0

他實際上想從持續時間中減去週末時間。 – Rahul

回答

3

出於舉例的目的,我們使用靜態@start@end日期,但實際上您可以將它們替換爲列名稱,並且所有這些值都將按每行重新計算。

SET @start = '2012-09-30'; 
SET @end = '2012-11-03'; 

SELECT 
    @raw_days := DATEDIFF(@end, @start)+1 'raw_days', 
    @full_weeks := FLOOR(@raw_days/7) 'full_weeks', 
    @odd_days := @raw_days - @full_weeks * 7 'odd_days', 
    @wday_start := DAYOFWEEK(@start) 'wday_start', 
    @wday_end := DAYOFWEEK(@end) 'wday_end', 
    @weekend_intrusion := @wday_start + @odd_days 'weekend_intrusion', 
    @extra_weekends  := 
     IF(@wday_start = 1, IF(@odd_days = 0, 0, 1), 
      IF(@weekend_intrusion > 7, 2, 
       IF(@weekend_intrusion > 6, 1, 0) 
      ) 
     ) 'extra_weekends', 
    @total_weekends  := @full_weeks * 2 + @extra_weekends 'total_weekends', 
    @total_workdays  := @raw_days - @total_weekends 'total_workdays' 

IF陳述歸結爲:

如果一週上週日開始,並沒有「奇」天,再沒有多餘的週末。如果奇怪的日子,那麼只能有一個週末,因爲它不可能延伸到週六,因爲這將是一個「完整的」星期。

否則,我們看看一週的剩餘部分是否延續過去的星期天。如果是這樣,請添加2個週末。否則,如果部分轉到星期六,則增加1個週末日。否則爲0

輸出:

+----------+------------+----------+------------+----------+-------------------+----------------+----------------+----------------+ 
| raw_days | full_weeks | odd_days | wday_start | wday_end | weekend_intrusion | extra_weekends | total_weekends | total_workdays | 
+----------+------------+----------+------------+----------+-------------------+----------------+----------------+----------------+ 
|  34 |   4 |  6 |   1 |  6 |     7 |    1 |    9 |    25 | 
+----------+------------+----------+------------+----------+-------------------+----------------+----------------+----------------+ 
1

這應該工作:

SELECT 
username, 
CONCAT(
    FLOOR(
     SUM(
      UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time) - 86400 * (
       (FLOOR(DATEDIFF(end_time, start_time)/7) * 2) + 
        IF(WEEKDAY(end_time) > 4, WEEKDAY(end_time) - 4, 0) + 
        IF(WEEKDAY(end_time) < WEEKDAY(start_time), 2, 0) 
      ) 
     )/86400 
    ), 
    ' days ', 
    TIME_FORMAT(
    SEC_TO_TIME(
     SUM(
      UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time) - 86400 * (
       (FLOOR(DATEDIFF(end_time, start_time)/7) * 2) + 
        IF(WEEKDAY(end_time) > 4, WEEKDAY(end_time) - 4, 0) + 
        IF(WEEKDAY(end_time) < WEEKDAY(start_time), 2, 0) 
      ) 
     ) % 86400 
    ), 
    '%H hours %i minutes' 
    ) 
) AS duration 
FROM table 
WHERE start_time > CONCAT(YEAR(CURDATE()) -1, '-12-31') 
GROUP BY username; 

,但它假定start_timeend_time週末期間從未下降。

查看http://sqlfiddle.com/#!2/d41d8/3587的工作示例。

如果你不關心一個額外的列,這個查詢可以簡化爲:

SELECT 
@diff := SUM(
    UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time) - 86400 * (
     (FLOOR(DATEDIFF(end_time, start_time)/7) * 2) + 
      IF(WEEKDAY(end_time) > 4, WEEKDAY(end_time) - 4, 0) + 
      IF(WEEKDAY(end_time) < WEEKDAY(start_time), 2, 0) 
    ) 
) AS duration_seconds, 
CONCAT(
    FLOOR(@diff/86400), 
    ' days ', 
    TIME_FORMAT(SEC_TO_TIME(@diff % 86400), '%H hours %i minutes') 
) AS duration 
FROM table 
WHERE start_time > CONCAT(YEAR(CURDATE()) -1, '-12-31') 
GROUP BY username; 
0

繼承人,我用我的時間維度表只是開發了大量溶液它允許開始和結束是在週末。

Select 
    f.id, f.dtStart, f.dtEnd, 
    sec_to_time(
    sum(
     case 
      -- don't count weekends 
      when day_in_week in (6,7) 
       then 0 
      -- start and end on same day 
      when date(f.dtStart) = date(f.dtEnd) 
       then UNIX_TIMESTAMP(f.dtEnd) - UNIX_TIMESTAMP(f.dtStart) 
      -- start period 
      when date(f.dtStart) = dt.date_value 
       then 24*60*60 - time_to_sec(time(f.dtStart)) 
      -- end period 
      when date(f.dtEnd) = dt.date_value 
       then time_to_sec(time(f.dtEnd)) 
      -- middle days 
      else 24*60*60 
     end 
    ) 
    ) INT_timediff, 
From fact f 
join dim_date dt 
    on dt.date_value between date(f.dtStart) and date(f.dtEnd) 
group by f.id, f.dtStart, f.dtEnd