MySQL TimeDiff排除周末

时间:2012-11-09 16:59:22

标签: mysql

我一直在MySQL表上使用TimeDiff来获取两个字段之间的区别,两者都是DateTime格式。这是我正在使用的查询,它也将持续时间限制到今年。

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

我遇到的问题是我一直在努力尝试如何从结果中排除周末。有人可以帮忙吗?

3 个答案:

答案 0 :(得分: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语句归结为:

如果一周从星期日开始,并且没有'奇数'天,则没有额外的周末日。如果奇数天,则只有1个周末,因为它不可能延伸到星期六,因为这将是一个“完整”的一周。

否则,我们会看到一周的剩余部分是否延续了周日。如果是这样,请添加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 :(得分: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;

答案 2 :(得分: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