Consider a customer care center which receives calls from customers, Starttime and Endtime denotes the conversation start time and end time. Missed calls are the calls where no conversation happened. For any missed call, the customer care returns the call.
For below given table,
CREATE TABLE CustomerCare (fromnumber INT, tonumber INT, starttime DATETIME,endtime DATETIME)
INSERT INTO CustomerCare (fromnumber,tonumber,starttime,endtime)
VALUES
(100,1800,'2019-08-13 18:40:00','2019-08-13 18:40:00'),
(1800,100,'2019-08-13 18:55:00','2019-08-13 18:57:00'),
(200,1800,'2019-08-13 19:30:00','2019-08-13 19:30:00'),
(1800,200,'2019-08-13 20:05:00','2019-08-13 20:10:00'),
(300,1800,'2019-08-13 21:00:00','2019-08-13 21:00:00'),
(1800,300,'2019-08-13 21:20:00','2019-08-13 21:25:00'),
(400,1800,'2019-08-13 07:00:00','2019-08-13 07:00:00'),
(500,1800,'2019-08-13 8:00:00','2019-08-13 8:05:00')
Find the number of return calls that were done by customer care within 30 minutes of the missed call.
Fourth row indicates one such return call.
Can anyone help a SQL query.
first you find the missed call, then you find the call back by customercare. And you find the time different
select *
from CustomerCare mc
cross apply -- get call back time
(
select top 1 *
from CustomerCare x
where x.fromnumber = mc.tonumber
and x.tonumber = mc.fromnumber
and x.starttime > mc.starttime
order by x.starttime
) cb
where mc.starttime = mc.endtime -- missed call
and datediff(minute, mc.starttime, cb.starttime) > 30 -- time different between
-- missed call and callback
@Squirrel beat me to it and my answer original answer was basically identical to his but I'll post what I put together as it also speaks to why APPLY is so excellent.
First, for optimal performance you would want a clustered index (or a covering index) on starttime. Eg
CREATE CLUSTERED INDEX CL_CustomerCare_starttime ON dbo.CustomerCare(starttime);
Next, here's how you would do this using a JOIN (since you mentioned it):
SELECT
cc.fromnumber,
cc.tonumber,
missedcalltime = CAST(CAST(cc.starttime AS TIME) AS CHAR(5)),
callbacktime = CAST(CAST(x.starttime AS TIME) AS CHAR(5)),
timetocallback = datediff(minute, cc.starttime, x.starttime)
FROM dbo.CustomerCare AS cc
JOIN dbo.CustomerCare AS x
ON cc.tonumber = x.fromnumber
AND x.tonumber = cc.fromnumber
AND cc.starttime < x.starttime
WHERE datediff(minute, cc.starttime, x.starttime) <= 30;
Now, the APPLY version (this is what I put together before I saw Squirrels answer):
SELECT
cc.fromnumber,
cc.tonumber,
missedcalltime = CAST(CAST(cc.starttime AS TIME) AS CHAR(5)),
callbacktime = CAST(CAST(x.starttime AS TIME) AS CHAR(5)),
timetocallback = cb.t
FROM dbo.CustomerCare AS cc
CROSS APPLY
(
SELECT TOP (1) x.starttime
FROM dbo.CustomerCare AS x
WHERE cc.tonumber = x.fromnumber
AND x.tonumber = cc.fromnumber
AND cc.starttime < x.starttime
ORDER BY x.starttime
) AS x
CROSS APPLY (VALUES(datediff(minute, cc.starttime, x.starttime))) AS cb(t)
WHERE cb.t <= @callback;
Both Return:
fromnumber tonumber missedcalltime callbacktime timetocallback
----------- ----------- -------------- ------------ --------------
100 1800 18:40 18:55 15
300 1800 21:00 21:20 20
The first cool thing about APPLY is how I can use it to alias values to make my code "DRYer." (DRY = Don't Repeat Yourself). Since the expression datediff(minute, cc.starttime, x.starttime)
is used multiple times, I can use APPLY to process the expression once then reference it multiple times as cb.t . Note the line:
CROSS APPLY (VALUES(datediff(minute, cc.starttime, x.starttime))) AS cb(t)
This is an example of how APPLY can make your code much cleaner and easier to debug.
The second and possibly more important example of how the powerful of APPLY is how you can join an outer query with a subquery. My two examples above are not Identical. Theoretically, if there were more than one "call back" within 30 minutes, the JOIN version would return them. Since this is likely an edge case, asking for the "TOP (1)" returned call within a half hour will be sufficient and will perform better. If you examine the execution plan for both queries - the join version reads more rows (28) when it self joints to CustomerCare, the APPLY version reads 22 rows. If you change TOP (1) to TOP ([anything greater than 1]) in the APPLY version it will read 28 rows. Again, this is another way you can use APPLY to tune performance.
Lastly, an important note about TOP in a subquery... If you remove the ORDER BY in the APPLY subquery the subquery will read 58 rows instead of 22 when an ORDER BY over a correctly indexed column is present.
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.