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
Could you possibly add and populate a 'sessionID' field in your reporting table? then it could be as simple as:
select sessionId, datediff(second, min(time),max(time))
from Trip_History_reporting
group by sessionId
The is pseudo TSQL.
The syntax is not correct.
Order all Rows.
Join on RowNumber + 1.
Only use even.
WITH Ordered AS
(
SELECT DATE, TIME, USER
ROW_NUMBER() AS RowNumber
FROM Trip_History_Reporting
)
select O1.USER, (O2.TIME - O1.TIME)
from order O1
join order O2
on O2.RowNumber = O1.RowNumber + 1
where O1%2 = 0
Related
So, let's say that I have a group of donors, and they make donations on an irregular basis. I can put the donor name, the donation amount, and the donation date into a table, but then I want to do a report that shows all of that information PLUS the value of all donations after that amount.
I know that I can parse through this using a loop, but is there a better way?
I'm cheating here by not bothering with the code that would go through and assign a transaction number by donor and ensure that everything is the right order. That's easy enough.
DECLARE #Donors TABLE (
ID INT IDENTITY
, Name NVARCHAR(30)
, NID INT
, Amount DECIMAL(7,2)
, DonationDate DATE
, AmountAfter DECIMAL(7,2)
)
INSERT INTO #Donors VALUES
('Adam Zephyr',1,100.00,'2017-01-14',NULL)
, ('Adam Zephyr',2,200.00,'2017-01-17',NULL)
, ('Adam Zephyr',3,150.00,'2017-01-20',NULL)
, ('Braden Yu',1,50.00,'2017-01-11',NULL)
, ('Braden Yu',2,75.00,'2017-01-19',NULL)
DECLARE #Counter1 INT = 0
, #Name NVARCHAR(30)
WHILE #Counter1 < (SELECT MAX(ID) FROM #Donors)
BEGIN
SET #Counter1 += 1
SET #Name = (SELECT Name FROM #Donors WHERE ID = #Counter1)
UPDATE d1
SET AmountAfter = (SELECT ISNULL(SUM(Amount),0) FROM #Donors d2 WHERE ID > #Counter1 AND Name = #Name)
FROM #Donors d1
WHERE d1.ID = #Counter1
END
SELECT * FROM #Donors
It seems like there ought to be a way to do this recursively, but I just can't wrap my head around it.
This would show the latest donation per Name which I presume is the donor and the total of all amounts donated by that person. Perhaps it's more appropriate to use NID for the partitions.
;with MostRecentDonations as (
select *,
row_number() over (partition by Name order by DonationDate desc) as rn,
sum(Amount) over (partition by Name) as TotalDonations
from #Donors
)
select * from MostRecentDonations
where rn = 1;
There's certainly no need to store a running total anywhere unless you have some kind of performance issue.
EDIT:
I've thought about your question and now I'm thinking that you just want a running total with all the transactions included. That's easy too:
select *,
sum(Amount) over (partition by Name order by DonationDate) as DonationsToDate
from #Donors
order by Name, DonationDate;
I am trying to modify my query to include a running total for each county in my report. Below is my working query with an attempt to use SUM OVER PARTITION commented out:
SELECT DATEPART(MONTH, r.received_date) AS [MonthID] ,
DATENAME(MONTH, r.received_date) AS [Month] ,
o.name AS [CountyName] ,
rsc.description AS [Filing] ,
COUNT(r.id) AS [Request_Total] ,
CAST (AVG(CAST (DATEDIFF(HOUR, received_date, completion_date) AS DECIMAL(8,2))) / 24 AS DECIMAL(8,2)) AS [Total_Time_Days]
--SUM(r.id) OVER (PARTITION BY o.name) AS [TotalFilings]
FROM dbo.requests AS [r]
INNER JOIN dbo.organizations AS [o] ON o.id = r.submitted_to_organiztion_id
INNER JOIN dbo.request_status_codes AS [rsc] ON rsc.code = r.request_status_code
WHERE r.submitted_to_organiztion_id < 68
AND r.request_type_code = 1
AND CAST(r.received_date AS DATE) >= '01/01/2016'
AND CAST(r.received_date AS DATE) <= '06/30/2016'
AND o.name = 'Alachua'
GROUP BY DATENAME(MONTH, r.received_date) ,
DATEPART(MONTH, r.received_date) ,
o.name ,
rsc.description
ORDER BY DATEPART(MONTH, r.received_date) ,
CountyName ,
Filing;
And the results look correct:
Perhaps I am misusing the SUM PARTITION BYbut my end goal is to add an additional column that will sum the filing types for each county by month.
For example, the additional column for the month of January should be 13,654 while February should be 14,238 and so on.
Could I get some advice on how to get this query working correctly? Thanks,
Not sure this is the best way or more efficient, but I was able to create a sub-query to obtain the results I wanted. I do believe a CTE or use of a Windows function would be better, but I haven't been able to get it to work. Here is my query however:
SELECT X.[MonthID] ,
X.[Month] ,
X.[CountyName] ,
X.[Filing] ,
X.[Avg_Time_Days] ,
SUM(X.Request_Total) AS [Total_Requests]
FROM ( SELECT DATEPART(MONTH, r.received_date) AS [MonthID] ,
DATENAME(MONTH, r.received_date) AS [Month] ,
o.name AS [CountyName] ,
rsc.description AS [Filing] ,
COUNT(r.id) AS [Request_Total] ,
CAST (AVG(CAST (DATEDIFF(HOUR, received_date,
completion_date) AS DECIMAL(8, 2)))
/ 24 AS DECIMAL(8, 2)) AS [Avg_Time_Days]
--, SUM(r.id) OVER (PARTITION BY o.name, rsc.description) AS [TotalFilings]
FROM dbo.requests AS [r]
INNER JOIN dbo.organizations AS [o] ON o.id = r.submitted_to_organiztion_id
INNER JOIN dbo.request_status_codes AS [rsc] ON rsc.code = r.request_status_code
WHERE r.submitted_to_organiztion_id < 68
AND r.request_type_code = 1
AND CAST(r.received_date AS DATE) >= '01/01/2016'
AND CAST(r.received_date AS DATE) <= '06/30/2016'
--AND o.name = 'Alachua'
GROUP BY DATENAME(MONTH, r.received_date) ,
DATEPART(MONTH, r.received_date) ,
o.name ,
rsc.description
--, r.id
--ORDER BY DATEPART(MONTH, r.received_date) ,
-- CountyName ,
-- Filing
) AS X
GROUP BY X.[MonthID] ,
X.[Month] ,
X.[CountyName] ,
X.[Filing] ,
X.[Avg_Time_Days]
ORDER BY X.[MonthID] ,
X.[Month] ,
X.[CountyName] ,
X.[Filing];
I am new to sql server.
I have been trying to write a query to get report name, reports first and last run time and date, scheduled by ,how often it was run and method of delivery (email or locations) for SQL Server 2005.
Please help me with this.
here
SELECT
'EXEC ReportServer.dbo.AddEvent #EventType=''TimedSubscription'', #EventData='''
+ CAST(a.SubscriptionID AS VARCHAR(40)) + '''' AS ReportCommand
, b.name AS JobName
, a.SubscriptionID
, e.name
, e.path
, d.description
, laststatus
, eventtype
, LastRunTime
, date_created
, date_modified
FROM ReportServer.dbo.ReportSchedule a JOIN msdb.dbo.sysjobs b
ON a.ScheduleID = b.name
JOIN ReportServer.dbo.ReportSchedule c
ON b.name = c.ScheduleID
JOIN ReportServer.dbo.Subscriptions d
ON c.SubscriptionID = d.SubscriptionID
JOIN ReportServer.dbo.Catalog e
ON d.report_oid = e.itemid
WHERE e.name = 'Sales_Report'
I'm trying to create a report that UNIONs two datasets. It takes (1) a bunch of orders for a specific customer within a date range, and UNIONs it with (headers and) (2) the method of shipping following by the average of the time interval between the order being placed and the order being sent.
The screenshot below shows that, in SQL Server, the query works perfectly. However, when I run this exact same query in Visual Studio 2008 to create a report for this, the actual value of the average turnaround time is empty.
As far as I can tell, in SQL Server the query works perfectly for whatever parameters I give it. I just can't figure out why in the report the average turnaround time is always blank.
The query I'm running is:
DECLARE #turnaroundInfo TABLE
(
[Owner Reference] VARCHAR(48),
[Project] VARCHAR(48),
[Carrier Type] VARCHAR(48),
[Created Date] DATETIME,
[Shipped Date] DATETIME,
[Turnaround Time (hours)] INT
)
INSERT INTO #turnaroundInfo
SELECT orders.ownerReference AS [Owner Reference], p.name AS [Project], types.name AS [Carrier Type], orders.createdSysDateTime AS [Created Date], shipments.shippedDate AS [Shipped Date], DATEDIFF(HOUR, orders.createdSysDateTime, shipments.shippedDate) AS [Turnaround Time (hours)]
FROM datex_footprint.Orders orders
INNER JOIN datex_footprint.Projects p ON orders.projectId = p.id
INNER JOIN datex_footprint.CarrierServiceTypes types ON orders.preferredCarrierServiceTypeId = types.id
INNER JOIN datex_footprint.OrderLines lines ON orders.id = lines.orderId
INNER JOIN datex_footprint.Shipments shipments ON lines.shipmentId = shipments.id
WHERE p.name IN (#project) AND types.name IN(#carrier)
-- Get only the type and date-ranged turnaround info we want
DECLARE #orders TABLE
(
[Owner Reference] VARCHAR(48),
[Project] VARCHAR(48),
[Carrier Type] VARCHAR(48),
[Created Date] DATETIME,
[Shipped Date] DATETIME,
[Turnaround Time (hours)] INT
)
INSERT INTO #orders
SELECT *
FROM #turnaroundInfo
WHERE [Turnaround Time (hours)] >= 0 AND [Created Date] BETWEEN #startDate AND #endDate
ORDER BY [Turnaround Time (hours)], [Carrier Type] ;
-- UNION the relevant turnaround infor with headers
SELECT * FROM #orders o /* All the orders in the date range for this project and the selected carrier(s) */
UNION ALL
SELECT 'Carrier' AS [Carrier Type], 'Avg Turnaround Time' AS [Average Turnaround], NULL AS Column3, NULL AS Column4, NULL AS Colummn5, NULL AS Column6
UNION ALL
SELECT o.[Carrier Type], CAST(AVG(o.[Turnaround Time (hours)]) AS NVARCHAR(24)) AS [Average Turnaround], NULL AS Column3, NULL AS Column4, NULL AS Colummn5, NULL AS Column6
FROM #orders o
GROUP BY o.[Carrier Type];
Does anybody know or see what I might be missing?
Any help would be appreciated!
It's not blank, it's just might not in the column you expected - I can see the value '24' in your screenshot.
I figured out what my mistake was.
The column about the value 24 and the header and 24 value column were sized differently. In SQL Server, it didn't seem to care, but in Visual Studio it saw the size difference and actually dropped the whole column from displaying.
After I adjusted the average value column to VARCHAR(48), which is what the column above it was sized to, it displayed properly again.
I'm pulling what hair I have left out! The resultset is all good but I now need to do something else. Here's the T-SQL
SELECT documentdate
, x.a.value('(Date)[1]','varchar(50)') as [Date]
, x.a.value('(ReadCode)[1]','varchar(50)') as [Read Code]
, x.a.value('(Rubric)[1]','varchar(200)') as [Rubric]
, x.a.value('(Notes)[1]','varchar(200)') as [Notes]
, x.a.value('(Notes1)[1]','varchar(200)') as [Notes1]
, x.a.value('(Episodicity)[1]','varchar(50)') as [Episodicity]
, REF.dbo.PATs.OptedOut
FROM
EPR.dbo.PCTX_CONT t INNER JOIN
REF.dbo.PATNumbers
ON t.PATNoID = REF.dbo.PATNumbers.PATNoID
INNER JOIN
REF.dbo.PATs ON
REF.dbo.PATNumbers.CurrentNo =
REF.dbo.PATs.ManorNo
CROSS APPLY
t.DocumentXML.nodes('//CONT') x(a)
WHERE REPLACE(REF.dbo.PATNumbers.CurrentNo,' ','')=123456789
The results are
2010-05-13 8I64.
2010-05-13 8I6C.
2010-02-09 8I24.
2010-02-09 8I65.
2010-02-09 8I26.
2010-02-09 8I6B.
2009-06-02 8I24.
2009-03-17 8I26.
2009-01-06 8I64.
2009-01-06 8I6C.
2006-11-14 8I74.
2006-11-14 8I75.
However what I need is the latest row for a code so the results would read
2010-05-13 8I64.
2010-05-13 8I6C.
2010-02-09 8I24.
2010-02-09 8I65.
2010-02-09 8I26.
2010-02-09 8I6B.
2006-11-14 8I74.
2006-11-14 8I75.
I am guessing that your codes are the Rubric column
select documentdate,[Date], [Read Code], [Rubric], [Notes], [Notes1],
[Episodicity],REF.dbo.PATs.OptedOut FROM
(
SELECT documentdate
, x.a.value('(Date)[1]','varchar(50)') as [Date]
, x.a.value('(ReadCode)[1]','varchar(50)') as [Read Code]
, x.a.value('(Rubric)[1]','varchar(200)') as [Rubric]
, x.a.value('(Notes)[1]','varchar(200)') as [Notes]
, x.a.value('(Notes1)[1]','varchar(200)') as [Notes1]
, x.a.value('(Episodicity)[1]','varchar(50)') as [Episodicity]
, REF.dbo.PATs.OptedOut
, row_number() over (partition by x.a.value('(Rubric)[1]','varchar(200)')
order by x.a.value('(Date)[1]','varchar(50)') desc) as rn
FROM
EPR.dbo.PCTX_CONT t INNER JOIN
REF.dbo.PATNumbers
ON t.PATNoID = REF.dbo.PATNumbers.PATNoID
INNER JOIN
REF.dbo.PATs ON
REF.dbo.PATNumbers.CurrentNo =
REF.dbo.PATs.ManorNo
CROSS APPLY
t.DocumentXML.nodes('//CONT') x(a)
WHERE REPLACE(REF.dbo.PATNumbers.CurrentNo,' ','')=123456789
) a where rn = 1