SQL Server,计算每天有多个休息时间的总小时数

时间:2015-01-27 11:19:31

标签: sql sql-server

我正在创建一个查询,以获取某人在一天内完成的总小时数,但每天的时间可能有多次中断。

这是我目前的查询。

SELECT     
   CHINA_VISION_DorEvents.DorCtrls_Ref, 
   CHINA_VISION_PubCards.CardCode, 
   CHINA_VISION_DorEvents.EventTM
FROM       
   CHINA_VISION_PubCards 
INNER JOIN
   CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE     
   (CHINA_VISION_PubCards.CardCode = '000006f1') 
   AND CHINA_VISION_DorEvents.DorCtrls_Ref = '16'
ORDER BY
   CONVERT(Date,CHINA_VISION_DorEvents.EventTM) DESC

此查询当前不会尝试计算已用时间,但以下是此结果,以便您可以查看数据的外观。

Ref CardCode          EventTM
---------------------------------------
16  000006f1    2015-01-27 07:32:35.000
16  000006f1    2015-01-26 07:38:02.000
16  000006f1    2015-01-26 12:30:54.000
16  000006f1    2015-01-26 13:03:28.000
16  000006f1    2015-01-26 17:28:47.000
16  000006f1    2015-01-23 07:31:10.000
16  000006f1    2015-01-23 12:22:50.000
16  000006f1    2015-01-23 12:47:51.000
16  000006f1    2015-01-23 17:00:20.000
16  000006f1    2015-01-22 07:35:03.000
16  000006f1    2015-01-22 12:28:13.000
16  000006f1    2015-01-22 13:03:12.000
16  000006f1    2015-01-22 16:55:56.000

正如你所看到的那样,大多数日子都有4条记录,我需要计算出它们的经过时间,例如对于26

07:38:02
12:30:54
elapsed time = 4 hours, 52 minutes and 52 seconds
13:03:28
17:28:47
Elapsed time = 4 hours, 25 minutes and 19 seconds

所以26日的总时间是9小时17分钟71

所以在结果中它看起来像

 Date     Elapsed
 2015-01-26   9:17:71

等等

我们无需在2-3之间进行计算,因为用户未在此处登录。

                         1   2        3         4
think of it like this   ON - OFF    BACK ON  - OFF

表格结构

 Name            type         allow null
 Reference          int         Unchecked
 DorCtrls_Ref       int         Checked
 EventsID           tinyint     Checked
 EventTM            datetime    Checked
 CardCode           varchar(50) Checked
 JustificationCode  tinyint     Checked
 RecordIndex        bigint      Checked
 Memo               varchar(50) Checked
 TempltCard         varchar(1024)Checked
 Templtlength        varchar(32)Checked
 TempltDir          varchar(50) Checked 

4 个答案:

答案 0 :(得分:2)

如果您不使用非常旧版本的SQL Server,这将适用于您:

测试数据:

CREATE TABLE Test(Ref int, CardCode varchar(20), EventTM datetime)
insert into Test
select 16,'000006f1','2015-01-27T07:32:35.000' union all
select 16,'000006f1','2015-01-26T07:38:02.000' union all
select 16,'000006f1','2015-01-26T12:30:54.000' union all
select 16,'000006f1','2015-01-26T13:03:28.000' union all
select 16,'000006f1','2015-01-26T17:28:47.000' union all
select 16,'000006f1','2015-01-23T07:31:10.000' union all
select 16,'000006f1','2015-01-23T12:22:50.000' union all
select 16,'000006f1','2015-01-23T12:47:51.000' union all
select 16,'000006f1','2015-01-23T17:00:20.000' union all
select 16,'000006f1','2015-01-22T07:35:03.000' union all
select 16,'000006f1','2015-01-22T12:28:13.000' union all
select 16,'000006f1','2015-01-22T13:03:12.000' union all
select 16,'000006f1','2015-01-22T16:55:56.000';

查询:

WITH ByDays AS ( -- Number the entry register in each day
SELECT 
    EventTm AS T,
    CONVERT(VARCHAR(10),EventTm,102) AS Day,
    FLOOR(CONVERT(FLOAT,EventTm)) DayNumber,
    ROW_NUMBER() OVER(PARTITION BY FLOOR(CONVERT(FLOAT,EventTm)) ORDER BY EventTm) InDay 
FROM Test
)
--SELECT * FROM ByDays ORDER BY T
,Diffs AS (
SELECT 
    E.Day,
    E.T ET, O.T OT, O.T-E.T Diff, 
    DATEDIFF(S,E.T,O.T) DiffSeconds -- difference in seconds
FROM 
    (SELECT BE.T, BE.Day, BE.InDay 
     FROM ByDays BE 
     WHERE BE.InDay % 2 = 1) E -- Even rows
INNER JOIN
    (SELECT BO.T, BO.Day, BO.InDay 
     FROM ByDays BO 
     WHERE BO.InDay % 2 = 0) O -- Odd rows
ON E.InDay + 1 = O.InDay -- Join rows (1,2), (3,4) and so on
   AND E.Day = O.Day --  in the same day
)
--SELECT * FROM Diffs

SELECT Day, 
    SUM(DiffSeconds) Seconds, 
    CONVERT(VARCHAR(8), 
    (DATEADD(S, SUM(DiffSeconds), '1900-01-01T00:00:00')),
    108) TotalHHMMSS -- The same, formatted as HH:MM:SS
FROM Diffs GROUP BY Day

结果如下所示。

Day         Seconds  TotalHHMMSS
2015.01.22  31554    08:45:54
2015.01.23  32649    09:04:09
2015.01.26  33491    09:18:11

查看相应的sql小提琴:http://sqlfiddle.com/#!3/e1d31/1

答案 1 :(得分:1)

根据您在问题中发布的结果,您可以尝试以下代码

CREATE TABLE #TEMP(Ref INT,CardCode VARCHAR(40),EventTM DATETIME)

INSERT INTO #TEMP
SELECT 16,  '000006f1',    '2015-01-27 07:32:35.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-26 07:38:02.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-26 12:30:54.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-26 13:03:28.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-26 17:28:47.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-23 07:31:10.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-23 12:22:50.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-23 12:47:51.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-23 17:00:20.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-22 07:35:03.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-22 12:28:13.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-22 13:03:12.000'
UNION ALL
SELECT 16,  '000006f1',    '2015-01-22 16:55:56.000'

<强> QUERY

;WITH CTE AS
(
    -- Gets row number  Order the date
    SELECT ROW_NUMBER() OVER( ORDER BY EventTM)RNO,  * 
    FROM #TEMP
)
,CTE2 AS
(
    -- Split to hours,minutes and seconds
    SELECT C1.*,C2.EventTM EM,DATEDIFF(S,C1.EventTM,C2.EventTM)DD,
    cast(
        (cast(cast(C2.EventTM as float) - cast(C1.EventTM as float) as int) * 24) 
        + datepart(hh, C2.EventTM - C1.EventTM)
        as INT)HH
    ,CAST(right('0' + cast(datepart(mi, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)MM 
    ,CAST(right('0' + cast(datepart(ss, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)SS    
    FROM CTE C1
    LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
    WHERE C1.RNO % 2 <> 0
),
CTE3 AS
(
    -- Sum the hours, minute and seconds
    SELECT CAST(EventTM AS DATE)EventTM,
    SUM(HH) HH,SUM(MM) MM,SUM(SS) SS
    FROM CTE2
    GROUP BY CAST(EventTM AS DATE)
)
-- Format the elapsed time
SELECT EventTM,
CASE WHEN MM >=60 THEN CAST(HH+1 AS VARCHAR(10)) END + ':' +
CASE WHEN MM >=60 THEN right('0' + CAST(MM-60 AS VARCHAR(10)),2) END + ':' + 
CAST(SS  AS VARCHAR(10))Elapsed
FROM CTE3

编辑:

从您的查询中,您可以使用以下代码

;WITH CTE AS
(
    -- Gets row number  Order the date
    SELECT ROW_NUMBER() OVER( ORDER BY CONVERT(DateTime,CHINA_VISION_DorEvents.EventTM))RNO,  
           CHINA_VISION_DorEvents.DorCtrls_Ref Ref, 
           CHINA_VISION_PubCards.CardCode, 
           CONVERT(DateTime,CHINA_VISION_DorEvents.EventTM) EventTM
    FROM   CHINA_VISION_PubCards INNER JOIN
           CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
    WHERE  (CHINA_VISION_PubCards.CardCode = '000006f1') 
     and   CHINA_VISION_DorEvents.DorCtrls_Ref= '16'
)
,CTE2 AS
(
    -- Split to hours,minutes and seconds
    SELECT C1.*,C2.EventTM EM,DATEDIFF(S,C1.EventTM,C2.EventTM)DD,
    cast(
        (cast(cast(C2.EventTM as float) - cast(C1.EventTM as float) as int) * 24) 
        + datepart(hh, C2.EventTM - C1.EventTM)
        as INT)HH
    ,CAST(right('0' + cast(datepart(mi, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)MM 
    ,CAST(right('0' + cast(datepart(ss, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)SS    
    FROM CTE C1
    LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
    WHERE C1.RNO % 2 <> 0
),
CTE3 AS
(
    -- Sum the hours, minute and seconds
    SELECT CAST(EventTM AS DATE)EventTM,
    SUM(HH) HH,SUM(MM) MM,SUM(SS) SS
    FROM CTE2
    GROUP BY CAST(EventTM AS DATE)
)
-- Format the elapsed time
SELECT EventTM,
CASE WHEN MM >=60 THEN CAST(HH+1 AS VARCHAR(10)) END + ':' +
CASE WHEN MM >=60 THEN right('0' + CAST(MM-60 AS VARCHAR(10)),2) END + ':' + 
CAST(SS  AS VARCHAR(10))Elapsed
FROM CTE3

答案 2 :(得分:1)

试试这个,输出正确。

your output is wrong.9:17:71 is wrong.it should be 9:18:11.

    Declare @t table(Ref int, CardCode varchar(20), EventTM datetime)
insert into @t
select  16,'000006f1','2015-01-27 07:32:35.000' union all
select  16,'  000006f1','2015-01-26 07:38:02.000' union all
select 16,'  000006f1','2015-01-26 12:30:54.000' union all
select 16,'  000006f1','2015-01-26 13:03:28.000' union all
select 16,'  000006f1','2015-01-26 17:28:47.000' union all
select 16,'  000006f1','2015-01-23 07:31:10.000' union all
select 16,'  000006f1','2015-01-23 12:22:50.000' union all
select 16,'  000006f1','2015-01-23 12:47:51.000' union all
select 16,'  000006f1','2015-01-23 17:00:20.000' union all
select 16,'  000006f1','2015-01-22 07:35:03.000' union all
select 16,'  000006f1','2015-01-22 12:28:13.000' union all
select 16,'  000006f1','2015-01-22 13:03:12.000' union all
select 16,'  000006f1','2015-01-22 16:55:56.000'

;with CTE as
(
select *,row_number()over(partition by dateadd(d,0,datediff(d,0,EventTM)) 
order by EventTM)rn  from @t 
)
,CTE1 as
(
select Ref,CardCode,EventTM, rn oddrn,0 TimeElapse from CTE where rn%2<>0
union all
select a.Ref,a.CardCode,a.EventTM, rn ,datediff(s,b.EventTM,a.EventTM) 
from CTE a
inner join CTE1 b on 
dateadd(d,0,datediff(d,0,a.EventTM))= dateadd(d,0,datediff(d,0,b.EventTM)) 
and a.ref=b.ref
and a.rn-b.oddrn=1 and a.rn%2=0
)

select EventTM,cast((convert(varchar(5),TimeElapse/3600) +':'+ 
convert(varchar(5),TimeElapse%3600/60) 
+':'+ convert(varchar(5),TimeElapse%60)) as datetime)  from 
(select dateadd(d,0,datediff(d,0,EventTM)) EventTM,sum(TimeElapse) TimeElapse 
from cte1 
where TimeElapse>0
group by dateadd(d,0,datediff(d,0,EventTM)))tbl

答案 3 :(得分:0)

我自己无法测试,但它可能会给你一个良好的开端。如果你想将它保存在SQL中,我会使用游标,尽管我可能更喜欢在CLR中执行它。其他人可能有更好的方法,但你可以试试这个:

Declare @olddate datetime,
    @date datetime
DECLARE @Table table (Ref int, CardCode varchar(20), EventTM datetime, ElapsedTime varchar(30))

Declare cur_mycursor Cursor fast_forward for

SELECT     CHINA_VISION_DorEvents.EventTM
FROM       CHINA_VISION_PubCards INNER JOIN
CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE     (CHINA_VISION_PubCards.CardCode = '000006f1') 
and      CHINA_VISION_DorEvents.DorCtrls_Ref= '16'
Order by CHINA_VISION_DorEvents.EventTM desc

Open cur_mycursor
Fetch next from cur_mycursor into @olddate

While @@FETCH_STATUS = 0
Begin
Fetch next from cur_mycursor into @date
INSERT INTO @Table (Ref, CardCode, EventTM, ElapsedTime)
SELECT     CHINA_VISION_DorEvents.DorCtrls_Ref, 
   CHINA_VISION_PubCards.CardCode, 
   CHINA_VISION_DorEvents.EventTM,
   case when Convert(varchar, @date, 112) = Convert(varchar, CHINA_VISION_DorEvents.EventTM, 112) 
        then Cast(datediff(mi, @date, CHINA_VISION_DorEvents.EventTM) / 1440 as varchar) + ' days ' +
             Cast(datediff(mi, @date, CHINA_VISION_DorEvents.EventTM) % 1440 / 60 as varchar) + ' hours ' +
             Cast(datediff(mi, @date, CHINA_VISION_DorEvents.EventTM) % 1440 %60 as varchar) + ' minutes'
        else '0'
    end as elapsedtime
FROM       CHINA_VISION_PubCards INNER JOIN
   CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE     (CHINA_VISION_PubCards.CardCode = '000006f1') 
and      CHINA_VISION_DorEvents.DorCtrls_Ref= '16' and CHINA_VISION_DorEvents.EventTM = @olddate
Order by CHINA_VISION_DorEvents.EventTM desc
Set @olddate = @date
end
close cur_mycursor

Select * from @Table order by EventTM asc

deallocate cur_mycursor