all groups > sql server programming > january 2007 >
You're in the

sql server programming

group:

Want to avoid using cursor



Want to avoid using cursor Kayda
1/18/2007 9:23:01 PM
sql server programming: Hello:

Currently I have a large SQL Server 2000 DTS package that does many
things--I was able to do everything except one without using cursors. I
want to get rid of the one instance I use the cursor however as it
holds things up.

This is a database that measures events in the life of a phone call. I
have two tables, one with one line representing the entire call,
another with multiple records for the call, representing times a
customer was put on hold and then released from hold. These hold events
are ordered only by time to the second, so might be out of order if two
events happened on the same second. My goal is to capture both the
number of times a call was put on hold, and the total time the call
spent on hold. Here is some sample data:

CallTable
TelephoneCallID CallAnsweredTime NumberTimesOnHold
TimeOnHold
1 9:00:53 3 (need
to calculate from below) 00:03:10 (from below)

HoldEventsTable
TelephoneCallID Time Event
1 9:01:12 CallPutOnHold
1 9:01:20 CallReleasedFromHold
1 9:03:13 CallPutOnHold
1 9:05:13 CallPutOnHold
1 9:05:13 CallReleasedFromHold
1 9:06:15 CallReleasedFromHold
1 9:07:16 CallHangsUp

Also, the call might be disconnected while it is on hold, so in that
case I would need the time difference between the time the call went on
hold and the time.

How can I do this besides line by line cursor analysis?

Thanks!
Kayda
Re: Want to avoid using cursor Razvan Socol
1/18/2007 10:36:57 PM
Hello, Kayda

Assuming the following DDL and sample data:

CREATE TABLE HoldEventsTable (
TelephoneCallID int NOT NULL,
Time datetime NOT NULL,
Event varchar(20) NOT NULL,
PRIMARY KEY (Time, TelephoneCallID, Event)
)

INSERT INTO HoldEventsTable VALUES (1, '20070115 9:01:12',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (1, '20070115 9:01:20',
'CallReleasedFromHold')
INSERT INTO HoldEventsTable VALUES (1, '20070115 9:03:13',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (1, '20070115 9:05:13',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (1, '20070115 9:05:13',
'CallReleasedFromHold')
INSERT INTO HoldEventsTable VALUES (1, '20070115 9:06:15',
'CallReleasedFromHold')
INSERT INTO HoldEventsTable VALUES (1, '20070115 9:07:16',
'CallHangsUp')

INSERT INTO HoldEventsTable VALUES (2, '20070115 9:02:12',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (2, '20070115 9:02:20',
'CallReleasedFromHold')
INSERT INTO HoldEventsTable VALUES (2, '20070115 9:03:13',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (2, '20070115 9:05:16',
'CallHangsUp')

INSERT INTO HoldEventsTable VALUES (3, '20070115 23:59:30',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (3, '20070116 00:01:20',
'CallReleasedFromHold')

You can get the expected result using this query:

SELECT TelephoneCallID, COUNT(*) AS NumberTimesOnHold,
CONVERT(varchar(8),DATEADD(s,
SUM(DATEDIFF(s, HoldStartTime, HoldEndTime))
,0),108) as TimeOnHold
FROM (
SELECT TelephoneCallID, Time as HoldStartTime, (
SELECT MIN(Time) FROM HoldEventsTable r
WHERE r.TelephoneCallID=h.TelephoneCallID
AND r.Time>h.Time
AND Event IN ('CallReleasedFromHold','CallHangsUp')
) as HoldEndTime
FROM HoldEventsTable h
WHERE Event='CallPutOnHold'
) X
GROUP BY TelephoneCallID

Razvan
Re: Want to avoid using cursor Alex Kuznetsov
1/19/2007 6:53:45 AM

[quoted text, click to view]

Razvan,

Thanks for the test data!
In 2005 it is somewhat shorter:

SELECT b.TelephoneCallID, b.[time] HoldBegan, COALESCE(e.[time],
(SELECT MAX([Time]) FROM HoldEventsTable h WHERE h.TelephoneCallID =
b.TelephoneCallID)) HoldEnded
FROM (
SELECT TelephoneCallID, [time], ROW_NUMBER() OVER(PARTITION BY
TelephoneCallID ORDER BY [time]) rn
FROM HoldEventsTable WHERE [Event] = 'CallPutOnHold'
) b LEFT JOIN (
SELECT TelephoneCallID, [time], ROW_NUMBER() OVER(PARTITION BY
TelephoneCallID ORDER BY [time]) rn
FROM HoldEventsTable WHERE [Event] IN ('CallReleasedFromHold',
'CallHangsUp')
) e ON b.TelephoneCallID = e.TelephoneCallID AND b.rn = e.rn


-----------------------
Alex Kuznetsov
http://sqlserver-tips.blogspot.com/
http://sqlserver-puzzles.blogspot.com/
Re: Want to avoid using cursor Roy Harvey
1/19/2007 7:49:23 AM
I though this one was interesting so I decided to see what I could
come up with. I used Razvan's table and test data. I did not bother
to convert the number of elapsed seconds to the time format.

My major concern was the cases when hold could start/end in the same
second. The approach I used was to assign a sequence number to each
type of event, then join the two types by that ordinal.

I also added more test data, including:

INSERT INTO HoldEventsTable VALUES (7, '20070116 8:00:10',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (7, '20070116 8:00:10',
'CallReleasedFromHold')
INSERT INTO HoldEventsTable VALUES (7, '20070116 8:02:25',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (7, '20070116 8:03:25',
'CallHangsUp')

The query below handles this case and returns an elapsed seconds of
60. The other solution returnd 00:04:15.

SELECT Starting.TelephoneCallID,
SUM(datediff(second, Starting.Time, Ending.Time)) as
ElapsedSeconds,
count(*) as TimesOnHold
FROM (SELECT TelephoneCallID,
Time,
(select count(*) from HoldEventsTable as B
where B.Event = 'CallPutOnHold'
and A.TelephoneCallID = B.TelephoneCallID
and A.Time >= B.Time) as Ordinal
FROM HoldEventsTable as A
WHERE Event = 'CallPutOnHold') as Starting
JOIN (SELECT TelephoneCallID,
Time,
(select count(*) from HoldEventsTable as D
where D.Event IN ('CallReleasedFromHold',
'CallHangsUp')
and C.TelephoneCallID = D.TelephoneCallID
and C.Time >= D.Time) as Ordinal
FROM HoldEventsTable as C
WHERE Event IN ('CallReleasedFromHold', 'CallHangsUp')) as
Ending
ON Starting.TelephoneCallID = Ending.TelephoneCallID
AND Starting.Ordinal = Ending.Ordinal
GROUP BY Starting.TelephoneCallID

Roy Harvey
Beacon Falls, CT

[quoted text, click to view]
Re: Want to avoid using cursor Razvan Socol
1/19/2007 7:53:14 AM
What do you think about this sample data:

INSERT INTO HoldEventsTable VALUES (8, '20070116 8:00:10',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (8, '20070116 8:02:25',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (8, '20070116 8:03:25',
'CallReleasedFromHold')
INSERT INTO HoldEventsTable VALUES (8, '20070116 8:03:35',
'CallPutOnHold')
INSERT INTO HoldEventsTable VALUES (8, '20070116 8:03:59',
'CallHangsUp')

Obviously, the sample data is wrong, but it's interesting to note that
all three queries posted so far, give different results: 4:39, 4:49 and
5:13. In my opinion, the correct result would be 3:39.

Razvan
Re: Want to avoid using cursor Alex Kuznetsov
1/19/2007 8:24:18 AM

[quoted text, click to view]

I would very much prefer to clean up the data instead of writing a
smart query to account for problems with it.
Also in most cases (but not all!) I would really prefer to store
durations, not times.
An obvious exception is a "security video camera" scenario when the
stored data is almost never selected from, unless something wrong
happens.
Having said that, I usually go for storing durations. I even can (and
sometimes do) enforce with RI that durations have no gaps and no
overlaps.

-----------------------
Alex Kuznetsov
http://sqlserver-tips.blogspot.com/
http://sqlserver-puzzles.blogspot.com/
Re: Want to avoid using cursor Roy Harvey
1/19/2007 11:06:43 AM
On 19 Jan 2007 07:53:14 -0800, "Razvan Socol" <rsocol@gmail.com>
[quoted text, click to view]

Correct result from incorrect data? Maybe the correct answer should
be NULL, as in Unknown.

I hate matching up events like that, I've experienced too much pain
doing it.

AddThis Social Bookmark Button