计算没有重叠时间的总时间

时间:2017-10-03 06:55:32

标签: sql sql-server

MSSQL 2016

目前我有以下表格:

https://i.stack.imgur.com/W4GX6.png

OwnerName = Jan今天工作时间为07.00至15:30。这意味着他的总工作时间应为8.5小时。然而,当我总结分钟时,由于他有重叠的活动,我得到了更多。我怎样才能排除这些重叠的活动并计算他在这一天的总工作时间: 2017年9月12日

现在,此表不仅包含2017-09-12的数据,而且每天都有计划的活动。所以查询应该考虑到这一点。此外,该表中有多个所有者,即Luc。这也是查询应该处理的问题。

有人能帮助我吗? :)

编辑:让事情更清楚。结果应采用以下格式

|------------------------|------------------|----------------|
|      Ownername         |     Date         |  Time in hours |
|------------------------|------------------|----------------|
|          Jan           |   2017-09-12     |   8.5          | 
|          Luc           |   2017-09-12     |   8.5          |
|          John          |   2017-09-12     |   8.5          |
|          Doe           |   2017-09-11     |   7            |
|          Jan           |   2017-09-13     |   4            | 
|          Doe           |   2017-09-14     |   8.5          |
|          Tom           |   2017-09-14     |   7            |

我目前的猜测是我首先必须确定哪些活动是重叠的。我想我必须使用BETWEEN语句来做到这一点。然后使用CASE语句将此时间添加到当天的总时间(小时)。

2 个答案:

答案 0 :(得分:0)

我认为您的问题是使用SUM关键字。而是尝试使用DATEDIFF。 这将是准确的,如果你每天有多个记录使用CURSOR,那么你可以独立地获取evry记录。

例如,在CURSOR中使用DATEDIFF,然后使用该结果

答案 1 :(得分:0)

好的,请尝试以下解决方案。它删除了其他一些包含的间隔,然后使用LAG窗口函数访问上一行并更新间隔以使其不重叠。然后我们可以简单地执行SUM

WITH
     inputs ( name, date_fr, date_to ) AS (
       select 'Dan'    , cast('07-Jun-17 08:00:00' as DATETIME), cast('07-Jun-17 10:00:00' as DATETIME) union all
       select 'Dan'  , cast('07-Jun-17 11:00:00' as DATETIME), cast('07-Jun-17 12:00:00' as DATETIME) union all
       select 'Jan' , cast('08-Jun-17 08:15:00' as DATETIME), cast('08-Jun-17 15:00:00' as DATETIME) union all
       select 'Jan' , cast('08-Jun-17 08:15:00' as DATETIME), cast('08-Jun-17 10:00:00' as DATETIME) union all
       select 'Jan'    , cast('07-Jun-17 08:30:00' as DATETIME), cast('07-Jun-17 15:00:00' as DATETIME) union all
       select 'Jan'    , cast('07-Jun-17 09:00:00' as DATETIME), cast('07-Jun-17 9:30:00' as DATETIME) union all
       select 'Jan' , cast('07-Jun-17 08:00:00' as DATETIME), cast('07-Jun-17 10:00:00' as DATETIME)
     )
-- End of simulated input (for testing only, not part of the solution).
SELECT name, year(date_fr) year, month(date_fr) month, day(date_fr) day, 
       sum(cast(datediff(minute, date_fr, date_to) as decimal(10,4)))/60
FROM
    (
        SELECT t.name,
               CASE WHEN lag(date_to) over (partition by name order by date_fr) > date_fr 
                    THEN lag(date_to) over (partition by name order by date_fr) 
                    ELSE date_fr 
               END date_fr, 
               t.date_to
        FROM
        (
            SELECT name, date_fr, date_to
            FROM   inputs i1
            WHERE NOT EXISTS (SELECT 1 FROm inputs i2 WHERE i1.name = i2.name and i2.date_fr < i1.date_fr and i2.date_to > i1.date_to) -- this removes fully nested intervals
        ) t
    ) tt
WHERE tt.date_fr < tt.date_to
GROUP BY name, year(date_fr), month(date_fr), day(date_fr)