| 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//columnB1//12//33//64//105//156//21 and so on, until 10 has been reachedAny 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. |
 |
|
|
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 torder 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. |
 |
|
|
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 torder 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. |
 |
|
|
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 columnBfrom tbl t1 inner join tbl t2 on t1.columnA >= t2.columnAgroup by t1.columnAorder by t1.columnA Regardless, the poster indicated that his actual calculation was somewhat more complex, so cursors may be required in the end anyway. |
 |
|
|
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. |
 |
|
|
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. |
 |
|
|
X002548
Not Just a Number
15586 Posts |
|
|
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? |
 |
|
|
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 answerBrett8-)Hint: Want your questions answered fast? Follow the direction in this linkhttp://weblogs.sqlteam.com/brettk/archive/2005/05/25/5276.aspxAdd 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. |
 |
|
|
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. |
 |
|
|
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. |
 |
|
|
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 |
 |
|
|
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. |
 |
|
|
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. |
 |
|
|
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)goset noCount ondeclare @i intset @i = 1while @i<=1000 begin insert into tbl(columnA) values(@i) set @i = @i+1endset noCount offgo 2. Test nr proposal (each script I run 10 times, commented numbers are execution times on my laptop)checkpointgodbcc dropCleanBuffersgoDBCC FREEPROCCACHEgodeclare @executionTime intdeclare @start dateTimeset @start = getDate()select columnA, columnB = (select sum(columnA) from tbl t2 where t2.columnA <= t.columnA)from tbl torder by columnAselect dateDiff(ms, @start, getDate())--440--330--350--440--420--440--313--303--360--390--total=3786go 3. The same for blindman's solution:checkpointgodbcc dropCleanBuffersgoDBCC FREEPROCCACHEgodeclare @executionTime intdeclare @start dateTimeset @start = getDate()select t1.columnA, sum(t2.columnA) as columnBfrom tbl t1 inner join tbl t2 on t1.columnA >= t2.columnAgroup by t1.columnAorder by t1.columnAselect dateDiff(ms, @start, getDate())--580--290--430--440--350--380--440--433--510--410--total=4263go 4. Finally cursor based solution:checkpointgodbcc dropCleanBuffersgoDBCC FREEPROCCACHEgoset noCount ondeclare @executionTime intdeclare @start dateTimeset @start = getDate()declare @columnA intdeclare @columnB intdeclare @result table(columnA int primary key clustered, columnB int not null)declare c cursor for select columnA from tbl order by columnAopen cset @columnB = 0while (1=1) begin fetch next from c into @columnA if @@fetch_status <> 0 break set @columnB = @columnB + @columnA insert into @result values(@columnA, @columnB)endclose cdeallocate cselect * from @result order by columnAselect dateDiff(ms, @start, getDate())set noCount off--150--150--160--160--150--100--150--170--150--153--total=1503go |
 |
|
|
Arnold Fribble
Yak-finder General
1961 Posts |
Posted - 2006-01-26 : 12:41:54
|
What's wrong withSELECT columnA, columnA * (columnA + 1) / 2 AS columnBFROM tblORDER 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. |
 |
|
|
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). |
 |
|
|
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 317blindman 398cursor 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 283blindman 392cursor 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) goset noCount ondeclare @i intset @i = 1while @i<=1000 begin insert into tbl(columnA) values(@i % 17) set @i = @i+1endset noCount offgodeclare @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 intdeclare @start dateTimedeclare @Races intset @Races = 1--2. Test nr proposal (each script I run 10 times, commented numbers are execution times on my laptop)while @Races > 0begin 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 - 1endselect Horse, avg(MilliSeconds) as AvgMillisecondsfrom @RaceResultsgroup by Horseorder by avg(MilliSeconds) ascdrop table tblgo |
 |
|
|
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. |
 |
|
|
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. |
 |
|
|
Next Page
|