Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 Transact-SQL (2000)
 looping with a calculation

Author  Topic 

dirwin26
Yak Posting Veteran

81 Posts

Posted - 2006-01-24 : 15:21:53
I want to create a loop that performs a calculation on columnA until finally all rows have been included in the calc and returns each succesive loop in columnB. For simplicity, i'll use a sum calculation:

columnA//columnB
1//1
2//3
3//6
4//10
5//15
6//21 and so on, until 10 has been reached

Any help would be greatly appreciated!

Dirwin

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-24 : 15:43:24
For a straight cumulative aggregation you can use a simple subquery. For more complex calculations it is very possible that you will need a cursor.
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2006-01-25 : 09:08:16
select columnA, columnB = (select sum(columnA) from tbl t2 where t2.columnA <= t.columnA)
from tbl t
order by columnA



==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-25 : 09:20:58
quote:
Originally posted by nr

select columnA, columnB = (select sum(columnA) from tbl t2 where t2.columnA <= t.columnA)
from tbl t
order by columnA



==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.

Reading your signature I couldn't resist: Cursor solution is faster then one you suggested.
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-25 : 10:31:40
Really? I'm not so sure of that, but I'd bet that this is faster than a cursor:
select	t1.columnA,
sum(t2.columnA) as columnB
from tbl t1
inner join tbl t2 on t1.columnA >= t2.columnA
group by t1.columnA
order by t1.columnA

Regardless, the poster indicated that his actual calculation was somewhat more complex, so cursors may be required in the end anyway.
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2006-01-25 : 10:33:44
Not saying that a cursor solution wouldn't sometimes be faster just that it probably shouldn't be implemented.
Usually indicates a problem with the design.

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-25 : 10:47:25
nr: I agree, just couldn't resist when I saw the problem, the solution offered and the signatrue.
blindman: I don't have time to test now, but if i were you I wouldn't bet without testing. If I had to bet on one solution before testing (yours against cursor) I would bet on cursor.
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2006-01-25 : 10:57:47
Why don't you read the hint link in my sig and provide us with some specific details an what you need.

99.9% of the time a CURSOR is not the answer



Brett

8-)

Hint: Want your questions answered fast? Follow the direction in this link
http://weblogs.sqlteam.com/brettk/archive/2005/05/25/5276.aspx

Add yourself!
http://www.frappr.com/sqlteam
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-25 : 12:00:08
quote:
Originally posted by mmarovicblindman: I don't have time to test now, but if i were you I wouldn't bet without testing. If I had to bet on one solution before testing (yours against cursor) I would bet on cursor.
Hey, if I knew the answer for certain, it wouldn't be gambling, would it? But I'll take the bet anyway. How about we play for a virtual Margarita?
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-25 : 12:55:35
quote:
Originally posted by X002548

Why don't you read the hint link in my sig and provide us with some specific details an what you need.

99.9% of the time a CURSOR is not the answer



Brett

8-)

Hint: Want your questions answered fast? Follow the direction in this link
http://weblogs.sqlteam.com/brettk/archive/2005/05/25/5276.aspx

Add yourself!
http://www.frappr.com/sqlteam


As I already said, I agree with everything you said in response. However, cursor solution for the problem you responded is faster then one you provided. I aslo beleive there is faster data set solution even for that problem.
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-25 : 12:57:37
quote:
Originally posted by blindman

quote:
Originally posted by mmarovicblindman: I don't have time to test now, but if i were you I wouldn't bet without testing. If I had to bet on one solution before testing (yours against cursor) I would bet on cursor.
Hey, if I knew the answer for certain, it wouldn't be gambling, would it? But I'll take the bet anyway. How about we play for a virtual Margarita?

Ok, I hope I'll have time tomorrow to prepare tests. If you can do it before, feel free to post the scripts so we can both test.
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-25 : 13:29:15
OK. But maybe mmarovic can test it, since what I provided was a set-based solution such as he is seeking.
Go to Top of Page

dirwin26
Yak Posting Veteran

81 Posts

Posted - 2006-01-26 : 09:40:17
I'll have to play around with your suggestions. Thanks for the help and the ideas.

Dirwin
Go to Top of Page

dirwin26
Yak Posting Veteran

81 Posts

Posted - 2006-01-26 : 09:44:46
By the way, the actual calculation is finding the annualized standard deviation of lognormal numbers in a time series. SO I will have a series of numbers, one number for each in in 2005 and I want to create a loop that runs through these numbers and produces a new calculated number for each incremental day.
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-26 : 10:33:35
Post your formula. That may be appropriate for a set-based solution.
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-26 : 11:53:41
I am drinking Margarita waiting for the formula to be posted

Here are test scripts:

1. Create and populate table:
create table tbl(
columnA int
)
go
set noCount on
declare @i int
set @i = 1
while @i<=1000 begin
insert into tbl(columnA) values(@i)
set @i = @i+1
end
set noCount off
go

2. Test nr proposal (each script I run 10 times, commented numbers are execution times on my laptop)
checkpoint
go
dbcc dropCleanBuffers
go
DBCC FREEPROCCACHE
go
declare @executionTime int
declare @start dateTime
set @start = getDate()
select columnA, columnB = (select sum(columnA) from tbl t2 where t2.columnA <= t.columnA)
from tbl t
order by columnA
select dateDiff(ms, @start, getDate())
--440
--330
--350
--440
--420
--440
--313
--303
--360
--390
--total=3786
go

3. The same for blindman's solution:
checkpoint
go
dbcc dropCleanBuffers
go
DBCC FREEPROCCACHE
go
declare @executionTime int
declare @start dateTime
set @start = getDate()
select t1.columnA,
sum(t2.columnA) as columnB
from tbl t1
inner join tbl t2 on t1.columnA >= t2.columnA
group by t1.columnA
order by t1.columnA
select dateDiff(ms, @start, getDate())
--580
--290
--430
--440
--350
--380
--440
--433
--510
--410
--total=4263
go
4. Finally cursor based solution:
checkpoint
go
dbcc dropCleanBuffers
go
DBCC FREEPROCCACHE
go
set noCount on
declare @executionTime int
declare @start dateTime
set @start = getDate()

declare @columnA int
declare @columnB int

declare @result table(columnA int primary key clustered, columnB int not null)

declare c cursor for select columnA from tbl order by columnA

open c

set @columnB = 0

while (1=1) begin
fetch next from c into @columnA
if @@fetch_status <> 0 break
set @columnB = @columnB + @columnA
insert into @result values(@columnA, @columnB)
end
close c
deallocate c

select * from @result order by columnA
select dateDiff(ms, @start, getDate())
set noCount off
--150
--150
--160
--160
--150
--100
--150
--170
--150
--153
--total=1503
go
Go to Top of Page

Arnold Fribble
Yak-finder General

1961 Posts

Posted - 2006-01-26 : 12:41:54
What's wrong with

SELECT columnA, columnA * (columnA + 1) / 2 AS columnB
FROM tbl
ORDER BY columnA



Actually, I wonder what the performance is like in SQL Server 2005 if you use a recursive CTE and ROW_NUMBER(). I'll try it later when I get home.
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-26 : 13:09:49
Yesss!!! The ultimate data set solution based on formula discovered by certain small less the 10 years old boy (if i remember well).
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-26 : 13:57:28
I got very different results running your code. Cursor was significantly slower each trial.

I then modified your code to make it more realistid. Added an Identity for ordering the data. The results without a primary key were:

Horse AvgMilliseconds
-------------------------------------------------- ---------------
nr 317
blindman 398
cursor 1815

Adding RecordID as primary key improved nr and blindman execution times a little bit, but significantly impeded the cursor-based solutiion:

Horse AvgMilliseconds
-------------------------------------------------- ---------------
nr 283
blindman 392
cursor 5053


Conclusion: Cursors suck.

Here is the code I used:

set nocount on

--1. Create and populate table:
CREATE TABLE [tbl]([RecordID] [int] IDENTITY (1, 1) NOT NULL , [columnA] [int] NOT NULL)
go
set noCount on
declare @i int
set @i = 1
while @i<=1000 begin
insert into tbl(columnA) values(@i % 17)
set @i = @i+1
end
set noCount off
go

declare @RaceResults table (Horse varchar(50), Milliseconds int)
declare @result table(RecorID int primary key clustered, columnA int not null, columnB int not null)
declare @executionTime int
declare @start dateTime

declare @Races int
set @Races = 1

--2. Test nr proposal (each script I run 10 times, commented numbers are execution times on my laptop)

while @Races > 0
begin
checkpoint
dbcc dropCleanBuffers
DBCC FREEPROCCACHE
set @start = getDate()
select t.RecordID, t.columnA, columnB = (select sum(columnA) from tbl t2 where t2.RecordID <= t.RecordID)
from tbl t
insert into @RaceResults(Horse, Milliseconds) select 'nr', dateDiff(ms, @start, getDate())

--3. The same for blindman's solution:
checkpoint
dbcc dropCleanBuffers
DBCC FREEPROCCACHE
set @start = getDate()
select t1.RecordID,
t1.ColumnA,
sum(t2.columnA) as columnB
from tbl t1
inner join tbl t2 on t1.RecordID >= t2.RecordID
group by t1.RecordID,
t1.columnA
insert into @RaceResults(Horse, Milliseconds) select 'blindman', dateDiff(ms, @start, getDate())

--4. Finally cursor based solution:
checkpoint
dbcc dropCleanBuffers
DBCC FREEPROCCACHE
set noCount on
set @start = getDate()
declare @RecordID int
declare @columnA int
declare @columnB int
declare c cursor for select RecordID, columnA from tbl order by RecordID
open c
set @columnB = 0
while (1=1) begin
fetch next from c into @RecordID, @columnA
if @@fetch_status <> 0 break
set @columnB = @columnB + @columnA
insert into @result values(@RecordID, @columnA, @columnB)
end
close c
deallocate c

select * from @result order by columnA
insert into @RaceResults(Horse, Milliseconds) select 'cursor', dateDiff(ms, @start, getDate())
set noCount off

delete from @Result

set @Races = @Races - 1
end

select Horse,
avg(MilliSeconds) as AvgMilliseconds
from @RaceResults
group by Horse
order by avg(MilliSeconds) asc

drop table tbl
go
Go to Top of Page

mmarovic
Aged Yak Warrior

518 Posts

Posted - 2006-01-26 : 14:59:14
Of course, you changed primary key in table variable so rows are not ordered by primary key anymore. You cheated. You just tested solutions for different problem.
Go to Top of Page

blindman
Master Smack Fu Yak Hacker

2365 Posts

Posted - 2006-01-26 : 15:14:26
No, as I said, I got pretty much the same results when I ran the original code. Cursor was significantly slower.

The example I posted was closer to the poster's request. He is not ordering by the same column he is summing.
Go to Top of Page
    Next Page

- Advertisement -