简体   繁体   中英

What will be the best possible way to find date difference?

I have a table for operators in which I want to calculate the time difference between two status (10-20) for the whole day . 在此输入图像描述

Here I want the time difference between "ActivityStatus" 10 and 20. we have total 3 bunch of 10-20 status in this pic. for last status there is no 20 status in this case it will take the last oa_createdDate (ie oa_id 230141).

My expected output for this operator is date diff between cl_id 230096 and 230102 , date diff between cl_id 230103 and 230107 , date diff between cl_id 230109 and cl_id 230141. Once I get these difference I want to sum all the date diff value to calculate busy time for that operator.

Thanks in advance .

I have a sneaking suspicion that the DateDiff() function is the function that you seek

http://www.w3schools.com/sql/func_datediff.asp

There's an easy way to do what I assume you want done with outer apply, like so:

select tmin.*, t.oa_CreateDate oa_CreateDate_20
    , datediff(minute, tmin.oa_CreateDate, t.oa_CreateDate) DiffInMinutes
from testtable t
cross apply 
    (select top 1 * 
    from testtable tmin 
    where tmin.oa_CreateDate < t.oa_CreateDate and tmin.oa_OperatorId = t.oa_OperatorId
    order by tmin.oa_CreateDate asc) tmin
where t.ActivityStatus = 20
and t.oa_CreateDate < (select min(oa_CreateDate) from testtable where ActivityStatus = 10 and oa_OperatorId = 1960)
and t.oa_OperatorId = 1960 
union all
select t.*
    , coalesce(a.oa_CreateDate,ma.MaxDate) oa_CreateDate_20 
    , datediff(minute, t.oa_CreateDate, coalesce(a.oa_CreateDate,ma.MaxDate)) DiffInMinutes
from testtable t
outer apply 
    (select top 1 a.oa_CreateDate
    from testtable a
    where a.oa_OperatorId = t.oa_OperatorId and a.ActivityStatus = 20 
    and t.oa_CreateDate < a.oa_CreateDate order by a.oa_CreateDate asc) a
outer apply 
  (select max(a2.oa_CreateDate) maxDate
  from testtable a2 
  where a2.oa_OperatorId = t.oa_OperatorId
  and t.oa_CreateDate < a2.oa_CreateDate) ma
where oa_OperatorId = 1960 
and ActivityStatus = 10
order by oa_CreateDate asc, oa_CreateDate_20 asc

You can see the fiddle here .

But of course, you have to give us the format / accurracy for the datediff comparison. And this assumes you will always have both Status 10 AND 20, and that their timestamp ranges never overlap.

EDIT: Updated the answer based on your comment, check the new script and fiddle. Now the script fill find all Status 10 - 20 datediffs, and in case no Status 20 exists after the last 10, then the latest existing timestamp after that Status 10 will be used instead.

EDIT 2: Updated with your comment below. But at this point the script is getting rather ugly. Unfortunately I don't have the time to clean it up, so I ask that next time you post a question, please make it as clear cut and clean as possible, since there's a lot less effort involved to answer a question once instead of editing 3 different variations along the ride. :)

This should work anyhow, the new section before the UNION ALL in the script will return results only if there are any Status 20's without preceding 10's. Otherwise it'll return nothing, and move to the main portion of the script as before. Fiddle has been updated as well.

This is one way of doing it.

The first OUTER APPLY will retrieve the next row with a status of 20 that is after the current created datetime.

The second OUTER APPLY will retrieve the next row after the current created datetime where there is no status 20.

SELECT
    o.*
    , COALESCE(NextStatus.oa_CreateDate, NextStatusIsNull.oa_CreateDate) AS NextTimestamp
    , COALESCE(NextStatus.ActivityStatus, NextStatusIsNull.ActivityStatus) AS NextStatus
    , DATEDIFF(MINUTE, o.oa_CreateDate,
            COALESCE(NextStatus.oa_CreateDate, NextStatusIsNull.oa_CreateDate))
    AS DifferenceInMinutes

FROM
    operators AS o
    OUTER APPLY
    (
        SELECT TOP 1
            oa_CreateDate
            , ActivityStatus

        FROM
            operators

        WHERE
            ActivityStatus = 20
            AND oa_CreateDate > o.oa_CreateDate

        ORDER BY
            oa_CreateDate
    ) AS NextStatus
    OUTER APPLY
    (
        SELECT TOP 1
            oa_CreateDate
            , ActivityStatus

        FROM
            operators

        WHERE
            NextStatus.oa_CreateDate IS NULL
            AND oa_CreateDate > o.oa_CreateDate

        ORDER BY
            oa_CreateDate
    ) AS NextStatusIsNull

WHERE
    ActivityStatus = 10

I have used some different test data because you used a picture from which I was unable to cut and paste. This should be easy to convert to your table:

Note this should also work with the none-existing start and end dates,

Also note this was done without any joins to optimize performance.

Test table and data:

DECLARE @t table(ActivityStatus int, oa_createdate datetime, oa_operatorid int)

INSERT @t values
(30, '2015-07-23 08:20', 1960),(20, '2015-07-23 08:24', 1960),
(10, '2015-07-23 08:30', 1960),(20, '2015-07-23 08:40', 1960),
(10, '2015-07-23 08:50', 1960),(50, '2015-07-23 09:40', 1960)

Query:

;WITH cte as
(
  SELECT
    ActivityStatus, 
    oa_createdate,
    oa_operatorid
  FROM @t
  WHERE ActivityStatus in (10,20)
  UNION ALL
  SELECT 20, max(oa_createdate), oa_operatorid
  FROM @t
  GROUP BY oa_operatorid
  HAVING 
    max(case when ActivityStatus = 20 then oa_createdate end) <
    max(case when ActivityStatus = 10 then oa_createdate end)
  UNION ALL
  SELECT 10, min(oa_createdate), oa_operatorid
  FROM @t
  GROUP BY oa_operatorid
  HAVING
    min(case when ActivityStatus = 20 then oa_createdate end) <  
    min(case when ActivityStatus = 10 then oa_createdate else '2999-01-01' end)
)
SELECT 
  cast(cast(sum(case when activitystatus = 10 then -1 else 1 end 
  * cast(oa_createdate as float)) as datetime) as time(0)) as difference_in_time,
  oa_operatorid
FROM cte
GROUP BY oa_operatorid

Result:

difference_in_time  oa_operatorid
01:04:00            1960

Data

create table #Table2 (oa_id int, oa_OperatorId int, ActivityStatus int, oa_CreateDate datetime)

insert into #Table2 
values (1, 1960,10,'2015-08-10 10:55:12.317')   
    ,(2, 1960,20,'2015-08-10 11:55:12.317')
    ,(3, 1960,30,'2015-08-10 14:55:12.317')
    ,(4, 1960,50,'2015-08-10 14:58:12.317')
    ,(5, 1960,10,'2015-08-10 15:55:12.317')
    ,(6, 1960,20,'2015-08-10 16:20:12.317')
    ,(7, 1960,10,'2015-08-10 16:30:12.317')
    ,(8, 1960,50,'2015-08-10 17:20:12.317')

Populate target table with the rows we are interested in

select oa_id, 
       oa_operatorid, 
       ActivityStatus, 
       oa_createDate, 
       rn = row_number() over (order by oa_id desc) 
into #Table 
from #Table2 
where ActivityStatus in (10, 20)

insert #Table
select top 1 
       oa_id, 
       oa_operatorid, 
       ActivityStatus, 
       oa_createDate, 
       0 
from #Table2 
order by oa_id desc

select * into #Table10 from #Table where ActivityStatus = 10

select * into #Table20 from #Table where ActivityStatus = 20
union
select * from #Table where rn = 0 /*add the last record*/
except
select * from #Table where rn = (select max(rn) from #Table) /**discard the first "20" record*/


/*free time info*/
select datediff(second, t10.oa_createDate, t20.oa_createDate) secondssincelast10, 
t20.* 
from #Table10 t10 join #Table20 t20
on t10.rn = t20.rn + 1
and t10.oa_OperatorId = t20.oa_OperatorId


/*Summarized info per operator*/
select sum(datediff(second, t10.oa_createDate, t20.oa_createDate)) totalbusytime, 
t20.oa_OperatorId
from #Table10 t10 join #Table20 t20
on t10.rn = t20.rn + 1
and t10.oa_OperatorId = t20.oa_OperatorId
group by t20.oa_OperatorId

Best way

DATEDIFF(expr1,expr2)

Example:

  CREATE TABLE pins
    (`id` int, `time` datetime)
;

INSERT INTO pins
    (`id`, `time`)
VALUES
    (1, '2013-11-15 05:25:25')
;
SELECT DATEDIFF(CURDATE(), `time`)
FROM `pins`

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM