SQL - 一天中的总时间

时间:2017-10-03 08:26:13

标签: sql sql-server sql-server-2016

我的表看起来很像下面的StackOverflow URL中显示的表: Calculating total time excluding overlapped time & breaks in SQLServer

我的表还包含一个OwnerID。每个人都有一个唯一的OwnerID,我可以轻松加入属于该ID的人名。

请求的结果应该与链接的URL一样,但是每个所有者。我尝试修改他的URL的选定答案,但这给了我以下错误:

The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

这是我尝试运行的查询...

 ;WITH addNR AS ( -- Add row numbers

SELECT StartDate, EndDate, ROW_NUMBER() OVER (ORDER BY StartDate, EndDate) AS RowID
FROM dbo.FollowUp AS T
WHERE StartDate > '2017-10-02 08:30:00.000'
), createNewTable AS ( -- Recreate table according overlap time

SELECT StartDate, EndDate, RowID 
FROM addNR
WHERE RowID = 1

UNION ALL

SELECT 
    CASE 
        WHEN a.StartDate <= AN.StartDate AND AN.StartDate <= a.EndDate THEN a.StartDate 
        ELSE AN.StartDate END AS StartTime, 
    CASE WHEN a.StartDate <= AN.EndDate AND AN.EndDate <= a.EndDate THEN a.EndDate 
        ELSE AN.EndDate END AS EndTime,
    AN.RowID 
FROM addNR AS AN
INNER JOIN createNewTable AS a
    ON a.RowID + 1 = AN.RowID

), getMinutes AS ( -- Get difference in minutes
SELECT DATEDIFF(MINUTE,StartDate,MAX(EndDate)) AS diffMinutes
FROM createNewTable
GROUP BY StartDate
)
SELECT SUM(diffMinutes) AS Result
FROM getMinutes

我替换了StartTime = StartDate和EndTime = EndDate,因为我的列被命名为..

Sample Data

3 个答案:

答案 0 :(得分:0)

好的,这是工作代码,我不确定性能。想法:创建1分钟精度的“日历”,为每个OwnerId填充它并计算记录数

DECLARE @table TABLE (OwnerId int,StartTime DateTime2, EndTime DateTime2)

INSERT INTO @table SELECT 1,'2014-10-01 10:30:00.000', '2014-10-01 12:00:00.000'
INSERT INTO @table SELECT 1,'2014-10-01 10:40:00.000', '2014-10-01 12:00:00.000'
INSERT INTO @table SELECT 1,'2014-10-01 10:42:00.000', '2014-10-01 12:20:00.000'
INSERT INTO @table SELECT 1,'2014-10-01 10:40:00.000', '2014-10-01 13:00:00.000'
INSERT INTO @table SELECT 1,'2014-10-01 10:44:00.000', '2014-10-01 12:21:00.000'
INSERT INTO @table SELECT 1,'2014-10-13 15:50:00.000', '2014-10-13 16:00:00.000'
----------------------------------------------------------------------------
INSERT INTO @table SELECT 2,'2014-10-01 10:30:00.000', '2014-10-01 12:00:00.000'
INSERT INTO @table SELECT 2,'2014-10-01 10:40:00.000', '2014-10-01 12:00:00.000'
INSERT INTO @table SELECT 2,'2014-10-01 10:42:00.000', '2014-10-01 12:20:00.000'



declare @period int, @start datetime;;
select @period=datediff(mi, MIN(starttime),MAX(endtime)),@start =MIN(StartTime) from @table;


declare @seconds table(num int identity(0,1),garbage bit not null);
insert into @seconds(garbage) values(0);
while( select COUNT(*) from @seconds) < @period
    insert into @seconds(garbage ) select garbage  from @seconds;

with a(ownerId, usedminute ) as 
(
    select distinct t.ownerID,s.num from @seconds s join @table t on   
    dateadd(mi,s.num, @start) between t.StartTime  and dateadd(s,-1,t.EndTime)
)
select ownerId, count(*) time_in_minutes from a group by ownerID;

答案 1 :(得分:0)

巧合@vitalygolub。

使用各种样本数据尝试我的脚本。还有时间日历表应该是永久表,所以它只是时间创建。

它不是递归的,所以它应该表现得更好。如果输出被抛出则可以避​​免不同。

    create table #tbl (ownerid int,StartTime datetime,enddate datetime);
insert into #tbl values
 (1,'2014-10-01 10:30:00.000','2014-10-01 12:00:00.000') -- 90 mins
,(1,'2014-10-01 10:40:00.000','2014-10-01 12:00:00.000') --  0 since its overlapped with     previous
,(1,'2014-10-01 10:42:00.000','2014-10-01 12:20:00.000') -- 20 mins excluding overlapped time
,(1,'2014-10-01 10:40:00.000','2014-10-01 13:00:00.000') -- 40 mins
,(1,'2014-10-01 10:44:00.000','2014-10-01 12:21:00.000') -- 0 previous ones have already covered this time range
,(1,'2014-10-13 15:50:00.000','2014-10-13 16:00:00.000') -- 10 mins

create table #Timetable(timecol time primary key  )
insert into #Timetable
select   dateadd(minute,(c.rn-1),'00:00')
from(
select top (24*60)  row_number()over(order by number)rn from 
master..spt_values order by number)c


SELECT c.ownerid
    ,cast(c.StartTime AS DATE)
    ,count(DISTINCT timecol) TimeMin
FROM #Timetable t
CROSS APPLY (
    SELECT *
    FROM #tbl c
    WHERE timecol >= cast(c.StartTime AS TIME)
        AND timecol < cast(c.enddate AS TIME)
    ) c
GROUP BY c.ownerid
    ,cast(c.StartTime AS DATE)


drop table #Timetable
drop table #tbl

答案 2 :(得分:0)

您可以在没有while循环的情况下使用派生的计数表和基于常规集的连接来执行此操作,因此可以非常有效地执行:

-- Define test data
declare @table table (ownerid int,starttime datetime2, endtime datetime2);

insert into @table select 1,'2014-10-01 10:30:00.000', '2014-10-01 12:00:00.000';
insert into @table select 1,'2014-10-01 10:40:00.000', '2014-10-01 12:00:00.000';
insert into @table select 1,'2014-10-01 10:42:00.000', '2014-10-01 12:20:00.000';
insert into @table select 1,'2014-10-01 10:40:00.000', '2014-10-01 13:00:00.000';
insert into @table select 1,'2014-10-01 10:44:00.000', '2014-10-01 12:21:00.000';
insert into @table select 1,'2014-10-13 15:50:00.000', '2014-10-13 16:00:00.000';
----------------------------------------------------------------------------
insert into @table select 2,'2014-10-01 10:30:00.000', '2014-10-01 12:00:00.000';
insert into @table select 2,'2014-10-01 10:40:00.000', '2014-10-01 12:00:00.000';
insert into @table select 2,'2014-10-01 10:42:00.000', '2014-10-01 12:20:00.000';

-- Query

declare @MinStartTime datetime;
declare @Minutes int;

-- Define data boundaries

select @MinStartTime = min(starttime)
      ,@Minutes = datediff(minute,min(starttime), max(endtime))+1
from @table;
                  -- Initial Numbers Table - 10 rows
with t(t)     as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
                  -- Create tally table of minutes by cross joining numbers table many times to generate 1m rows
    ,n(n)     as (select top(@Minutes) dateadd(minute,row_number() over (order by (select null))-1,@MinStartTime) from t t1, t t2, t t3, t t4, t t5, t t6)
                  -- Define largest possible range for each OwnerID
    ,o(i,s,e) as (select ownerid, min(starttime), max(endtime) from @table group by ownerid)
select o.i as OwnerID
      ,cast(n.n as date) as DateValue
      ,count(n.n) as TotalMinutes
from o
    join n        -- Return minutes for each OwnerID range,
        on n.n between o.s and o.e
where exists(select null            -- where that minute should be included.
             from @table as t
             where n.n >= t.starttime
               and n.n < t.endtime)
group by o.i
        ,cast(n.n as date)
order by o.i
        ,DateValue

输出:

+---------+------------+--------------+
| OwnerID | DateValue  | TotalMinutes |
+---------+------------+--------------+
|       1 | 2014-10-01 |          150 |
|       1 | 2014-10-13 |           10 |
|       2 | 2014-10-01 |          111 |
+---------+------------+--------------+