Well I finally got your code to return the correct answer once I'd fixed all of the bugs...
ORDER
and
DATE
are reserved words, so if you are going to use them as table or column names you must surround them with square brackets i.e.
[order], [DATE]
. Better still, choose different names. You also did not declare
@row4
.
In the code you have presented there is no need for a cursor at all because you have used
ROW_NUMBER
meaning you can target the 4th row directly - which is essentially what you have done with
where row=4
NOTE: It is just as well that the query forming the cursor returns only a single row as you have the
FETCH NEXT
within an
ELSE
clause. If you are going to use a cursor then make sure you
always fetch the next row otherwise you could end up in an infinite loop.
The following trivial example shows how to find the 4th row in a query using a cursor (CAVEAT - this is not a recommended approach and is used to demonstrate the cursor only)
I set up some sample data that contains an int with the numbers 1 to 19 inclusive and a varchar column that contains some text (the word 'text' followed by the same number as the id)
create table cursorExample
(
id int identity(1,1),
someData varchar(max)
)
declare @i int = 1
while @i < 20
begin
insert into cursorExample values ('text' + cast(@i as varchar))
set @i = @i + 1
end
Now imagine I want to find the 4th row from the end of that table. I.e. the 4th row of this query
select id, someData from cursorExample order by id DESC
I can do that with the following sql
declare @myInt int
declare @myVarchar varchar(max)
declare @myDesiredInt int = 4
declare @myFoundVarchar varchar(max)
declare @myFoundInt int
declare @loopCount int = 1
declare myCursor cursor for
select id, someData from cursorExample order by id DESC
open myCursor
fetch next from myCursor into @myInt, @myVarChar
while @@FETCH_STATUS = 0
begin
print cast(@loopCount as varchar) + ' : '
+ @myVarChar + ' : ' + cast(@myInt as varchar) + ' : '
+ isnull(cast(@myFoundInt as varchar), 'null') + ' : '
+ isnull(@myFoundVarchar,'null')
if @loopCount = @myDesiredInt
begin
set @myFoundVarchar = @myVarchar
set @myFoundInt = @myInt
end
set @loopCount = @loopCount + 1
fetch next from myCursor into @myInt, @myVarChar
end
close myCursor
deallocate myCursor
print @myFoundVarchar
print @myFoundInt
Points to note:
- I'm using
@loopcount
to count the rows fetched from the cursor - I only set the "output" variables when I find the row I want.
- I increment
@loopcount
unconditionally within each loop
- I fetch the next row of the cursor unconditionally within the loop
- That long-winded print statement within the loop is just to demonstrate what is going on i.e.
1 : text19 : 19 : null : null
2 : text18 : 18 : null : null
3 : text17 : 17 : null : null
4 : text16 : 16 : null : null
5 : text15 : 15 : 16 : text16
6 : text14 : 14 : 16 : text16
7 : text13 : 13 : 16 : text16
8 : text12 : 12 : 16 : text16
9 : text11 : 11 : 16 : text16
10 : text10 : 10 : 16 : text16
11 : text9 : 9 : 16 : text16
12 : text8 : 8 : 16 : text16
13 : text7 : 7 : 16 : text16
14 : text6 : 6 : 16 : text16
15 : text5 : 5 : 16 : text16
16 : text4 : 4 : 16 : text16
17 : text3 : 3 : 16 : text16
18 : text2 : 2 : 16 : text16
19 : text1 : 1 : 16 : text16
Note - because the
print
statement is at the start of the loop then the desired results don't start appearing until the 5th loop.
Notice that the cursor runs "until the end" - which is one of the reasons that cursors can be a very inefficient way to extract data.
It is possible to "break out" of the cursor loop by introducing another variable
declare @abortLoop int = 0
, change the
WHERE
to be
while (@@FETCH_STATUS = 0 and @abortLoop = 0)
and set the new variable to another value once the desired values are found
set @myFoundVarchar = @myVarchar
set @myFoundInt = @myInt
set @abortLoop = 1
The output will then be
1 : text19 : 19 : null : null
2 : text18 : 18 : null : null
3 : text17 : 17 : null : null
4 : text16 : 16 : null : null
text16
16