I have a table that tracks user interactions with a customer’s record. I want to track the duration of each “touch” by a user, that is, the amount of time spent by each user every time we are required to access the customer’s record. I’ve got the basics down by using ROW_NUMBER() OVER (PARTITION BY | ORDER BY). The issue that I can’t get my pea brain around is how to separate two distinct user touches when those touches are sequential, but separated by a significant amount of time. I don’t know that I’ve explained that clearly, but the examples below should clarify:
Here is an example of data that I can query successfully:
DATE TIME USER
11/17/2011 1:30:47 ZDBatch
11/17/2011 1:32:40 ZDBatch
12/13/2011 10:39:46 EMSZC27
12/13/2011 10:45:48 EMSZC27
The desired result set is
DURATION (MIN) USER
1.883 ZDBatch
6.033 EMSZC27
and is achieved with the following query (note that I’ve left some columns out of the above example):
; WITH CTE1 AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY thr.[tdate], thr.[job], thr.[who], thr.[moddate] ORDER BY thr.[moddate]) AS RowNo,
thr.[tdate], thr.[job], thr.[moddate], thr.[modtime], thr.[seq], thr.[who],
thr.[description], thr.[histtype], thr.[attachment], thr.[data1], thr.[data2]
FROM Trip_History_Reporting thr
WHERE thr.[tdate] = '2011-10-24' --AND thr.[job] IN ('0653-A', '0128-A')
AND LEFT(thr.[description], 8) <> 'Recalled'
AND (LEFT(thr.[who],5) = 'EMSZC' OR thr.[who] = 'ZDBatch')
--ORDER BY thr.[moddate], thr.[modtime]
)
SELECT tdate, job, MIN(RowNo), MAX(RowNo), MIN(modtime) AS [Start], MAX(modtime) AS [End],
CAST(CAST(DATEDIFF(SECOND, MIN(modtime), MAX(modtime)) AS DECIMAL(6,0))/60 AS DECIMAL(8,2)) AS [Duration (Min)],
who, moddate
FROM CTE1
GROUP BY tdate, job, who, moddate
Here is an example of data that I cannot query successfully (sequential distinct touches by the same user):
DATE TIME USER
11/1/2011 6:34:48 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/15/2011 11:08:32 EMSZC34
11/15/2011 11:09:14 EMSZC34
11/15/2011 11:09:14 EMSZC34
11/15/2011 11:09:14 EMSZC34
11/15/2011 11:09:14 EMSZC34
11/15/2011 11:09:14 EMSZC34
The issue arises when a “significant” amount of time passes between the end of the first touch and the beginning of the second touch (for argument’s sake, if an hour passes between touches, those are distinct touches).
The desired result set from the above data is
DURATION (MIN) USER
0.333 EMSZC34
0.7 EMSZC34
This seems so simple, but I cannot figure it out. Thanks in advance for any ideas, suggestions, outright mockery at my thickheadedness.
I apologize for the delay in responding. I realize that this group is providing free assistance and I do not intend to take advantage of that by not replying quickly. I will do my best to not let this happen again.
Pursuant to EricZ's request, here is the example with the desired output:
DATE TIME USER
11/1/2011 6:34:48 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/1/2011 6:35:08 EMSZC34
11/15/2011 11:08:32 EMSZC34
11/15/2011 11:09:14 EMSZC34
11/15/2011 11:09:14 EMSZC34
11/15/2011 11:15:24 EMSZC34
11/15/2011 11:26:38 EMSZC34
11/15/2011 11:34:55 EMSZC34
11/15/2011 11:36:22 EMSZC34
Desired output:
DURATION (MIN) USER
0.333 EMSZC34
27.833 EMSZC34
Thanks in advance for any assistance you can provide. I've been researching islands and gaps in an attempt to resolve this dilemma, but I still can't figure it out.
Please try this. SQL Fiddle
;WITH c1 AS (
SELECT DISTINCT CAST(LEFT(CONVERT( VARCHAR(20),moddate,112),10)+ ' ' + modtime AS DATETIME)as moddate ,moduser
FROM Trip_History_Reporting
)
, c2 AS (
SELECT moddate,moduser,ROW_NUMBER() OVER (PARTITION BY moduser ORDER BY moddate) as row_no
FROM c1
)
, c3 AS (
SELECT c.moddate as startdate, cc.moddate as enddate, DATEDIFF(SECOND,c.moddate,cc.moddate) as diff,c.moduser
FROM c2 c
INNER JOIN c2 cc
ON c.moduser= cc.moduser
AND c.row_no = cc.row_no -1
)
SELECT *,diff/60.00 as diff_in_min
FROM c3
WHERE diff <= 60*60 -- an hour in second
UPDATE: Base on your updated question, please use the following query
WITH c1 AS (
SELECT DISTINCT CAST(LEFT(CONVERT( VARCHAR(20),moddate,112),10)+ ' ' + modtime AS DATETIME)as moddate ,moduser
FROM Trip_History_Reporting
)
, c2 AS (
SELECT moddate,moduser,(SELECT MAX(cc.moddate) FROM c1 cc WHERE cc.moddate <= DATEADD(hour,1,c.moddate) AND cc.moddate > c.moddate AND c.moduser = cc.moduser) AS endtime
FROM c1 c
)
, c3 AS (
SELECT moduser, MIN(moddate) as [start],endtime AS [end]
FROM c2
WHERE endtime IS NOT NULL
GROUP BY moduser,endtime
)
SELECT *,DATEDIFF(SECOND,[start],[end])/60.0 as diff_in_min
FROM c3