1

我一直在 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

我遇到的问题是我一直难以弄清楚如何从结果中排除周末。有人可以帮忙吗?

4

3 回答 3

4

出于示例的目的,我们使用静态@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 |
+----------+------------+----------+------------+----------+-------------------+----------------+----------------+----------------+
于 2012-11-09T21:42:51.410 回答
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
于 2013-11-06T10:22:01.203 回答
0

这应该有效:

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_time并且end_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;
于 2012-11-09T22:10:35.047 回答