Cursors in tsql, it can be done …
Simple cursor loop
Example below is the most straight forward example of walking through a CURSOR with a WHILE loop in tsql. The cursor is declared, opened and and a FETCH NEXT is done while @@fetch_status equals 0.
The behaviour of a FETCH [ NEXT ] call is that it returns the value(s) for the column(s) specified in the DECLARE CURSOR as a single-row result set.
Basic cursor example with FETCH returning row as single-row result set
1> declare c1 cursor 2> for 3> select left( name, 32) as name, 4> cast( dbid as varchar(10)) as dbid 5> from master.dbo.sysdatabases 6> where dbid < 5 7> 8> open c1 9> fetch next from c1 10> 11> -- while @@fetch_status != -1 and @@fetch_status != -2 12> while @@fetch_status = 0 13> fetch next from c1 14> 15> close c1 16> deallocate c1 17> 18> go name dbid -------------------------------- ---------- master 1 name dbid -------------------------------- ---------- tempdb 2 name dbid -------------------------------- ---------- model 3 name dbid -------------------------------- ---------- msdb 4 name dbid -------------------------------- ---------- 1>
@@fetch_status
@@fetch_status is a built in variable returning the exit code of the latest fetch call.
Return Value | Description |
---|---|
0 | FETCH statement was successful. |
-1 | FETCH statement failed or the row was beyond the result set. |
-2 | Row fetched is missing. |
FETCH INTO
The ‘FETCH INTO’ option is used to return values from a cursor (instead of getting it returned as a single-row result set) into pre-defined variables.
Example below displays how to perform a FETCH INTO with a single column.
1> declare @s_name varchar(32) 2> declare c1 cursor 3> for 4> select name 5> from master.dbo.sysdatabases 6> where dbid < 5 7> 8> open c1 9> fetch next from c1 into @s_name 10> 11> while @@fetch_status = 0 12> begin 13> print 'name = ' + @s_name 14> fetch next from c1 into @s_name 15> end 16> 17> close c1 18> deallocate c1 19> 20> go name = master name = tempdb name = model name = msdb 1>
Example below displays how to perform a FETCH INTO with multiple columns.
1> declare @s_name varchar(32), 2> @i_dbid smallint 3> declare c1 cursor 4> for 5> select name, 6> dbid 7> from master.dbo.sysdatabases 8> where dbid < 5 9> 10> open c1 11> fetch next from c1 into @s_name, @i_dbid 12> 13> while @@fetch_status = 0 14> begin 15> print 'DB = ' + cast( @i_dbid as varchar(8)) + ':' + @s_name 16> fetch next from c1 into @s_name, @i_dbid 17> end 18> 19> close c1 20> deallocate c1 21> 22> go DB = 1:master DB = 2:tempdb DB = 3:model DB = 4:msdb 1>
Scrollable cursors
All examples above are so-called ‘forward only cursors’ (the default cursor type). When you want to be able to scroll through your cursor the cursor most be declared through the SCROLL CURSOR declaration.
Whenever a cursor is declared as a SCROLL CURSOR the following FETCH options become available:
Fetch Options
- PRIOR
- FIRST
- LAST
- ABSOLUTE { n | @nvar }
- RELATIVE { n | @nvar }
All examples below are done with ‘demo’ table listed below.
demo table
1> select * from demo order by k 2> go k ----------- 0 1 2 3 4 5 6 7 8 9 (10 rows affected) 1>
FETCH NEXT and FETCH PRIOR
1> declare @i as int 2> declare c1 scroll cursor 3> for 4> select k 5> from demo 6> order by k 7> 8> 9> 10> open c1 11> 12> 13> print 'FETCH NEXT ' + char(10) 14> print '==================================' 15> 16> fetch next from c1 into @i 17> while @@fetch_status = 0 18> begin 19> print cast(@i as char(1)) + ' @@fetch_status ' + cast(@@fetch_status as char) 20> fetch next from c1 into @i 21> end 22> 23> fetch next from c1 into @i 24> print cast(@i as char(1)) + ' @@fetch_status ' + cast(@@fetch_status as char) 25> 26> 27> print char(10) + 'FETCH PRIOR ' + char(10) 28> print '==================================' 29> 30> fetch prior from c1 into @i 31> while @@fetch_status = 0 32> begin 33> print cast(@i as char(1)) + ' @@fetch_status ' + cast(@@fetch_status as char) 34> fetch prior from c1 into @i 35> end 36> 37> fetch prior from c1 into @i 38> print cast(@i as char(1)) + ' @@fetch_status ' + cast(@@fetch_status as char) 39> 40> 41> close c1 42> deallocate c1 43> 44> 45> 46> go FETCH NEXT ================================== 0 @@fetch_status 0 1 @@fetch_status 0 2 @@fetch_status 0 3 @@fetch_status 0 4 @@fetch_status 0 5 @@fetch_status 0 6 @@fetch_status 0 7 @@fetch_status 0 8 @@fetch_status 0 9 @@fetch_status 0 9 @@fetch_status -1 FETCH PRIOR ================================== 9 @@fetch_status 0 8 @@fetch_status 0 7 @@fetch_status 0 6 @@fetch_status 0 5 @@fetch_status 0 4 @@fetch_status 0 3 @@fetch_status 0 2 @@fetch_status 0 1 @@fetch_status 0 0 @@fetch_status 0 0 @@fetch_status -1 1>
FETCH LAST / FETCH PRIOR
1> declare @i as int 2> declare c1 scroll cursor 3> for 4> select k 5> from demo 6> order by k 7> 8> 9> 10> open c1 11> 12> 13> print char(10) + 'FIRST LAST PRIOR ' + char(10) 14> print '==================================' 15> fetch last from c1 into @i 16> print 'FETCH LAST = ' + cast(@i as char(1)) + 17> ' @@fetch_status = ' + cast(@@fetch_status as char) 18> fetch prior from c1 into @i 19> print 'FETCH PRIOR = ' + cast(@i as char(1)) + 20> ' @@fetch_status = ' + cast(@@fetch_status as char) 21> fetch first from c1 into @i 22> print 'FETCH FIRST = ' + cast(@i as char(1)) + 23> ' @@fetch_status = ' + cast(@@fetch_status as char) 24> fetch prior from c1 into @i 25> print 'FETCH PRIOR = ' + cast(@i as char(1)) + 26> ' @@fetch_status = ' + cast(@@fetch_status as char) 27> fetch next from c1 into @i 28> print 'FETCH NEXT = ' + cast(@i as char(1)) + 29> ' @@fetch_status = ' + cast(@@fetch_status as char) 30> fetch next from c1 into @i 31> print 'FETCH NEXT = ' + cast(@i as char(1)) + 32> ' @@fetch_status = ' + cast(@@fetch_status as char) 33> 34> 35> close c1 36> deallocate c1 37> 38> 39> 40> go FIRST LAST PRIOR ================================== FETCH LAST = 9 @@fetch_status = 0 FETCH PRIOR = 8 @@fetch_status = 0 FETCH FIRST = 0 @@fetch_status = 0 FETCH PRIOR = 0 @@fetch_status = -1 FETCH NEXT = 0 @@fetch_status = 0 FETCH NEXT = 1 @@fetch_status = 0 1>
FETCH ABSOLUTE
1> declare @i as int 2> declare c1 scroll cursor 3> for 4> select k 5> from demo 6> order by k 7> 8> open c1 9> 10> print char(10) + 'FETCH ABSOLUTE ' + char(10) 11> print '======================================' 12> fetch absolute 2 from c1 into @i 13> print 'FETCH ABSOLUTE 2 = ' + cast(@i as char(1)) + 14> ' @@fetch_status = ' + cast(@@fetch_status as char) 15> fetch absolute 8 from c1 into @i 16> print 'FETCH ABSOLUTE 8 = ' + cast(@i as char(1)) + 17> ' @@fetch_status = ' + cast(@@fetch_status as char) 18> 19> close c1 20> deallocate c1 21> 22> go FETCH ABSOLUTE ====================================== FETCH ABSOLUTE 2 = 1 @@fetch_status = 0 FETCH ABSOLUTE 8 = 7 @@fetch_status = 0 1>
FETCH RELATIVE
1> declare @i as int 2> declare c1 scroll cursor 3> for 4> select k 5> from demo 6> order by k 7> 8> 9> 10> open c1 11> 12> 13> print char(10) + 'FETCH RELATIVE' + char(10) 14> print '=======================================' 15> fetch relative 1 from c1 into @i 16> print 'FETCH RELATIVE 1 = ' + cast( @i as varchar(1)) + 17> ' @@fetch_status = ' + cast(@@fetch_status as char) 18> 19> fetch relative -1 from c1 into @i 20> print 'FETCH RELATIVE -1 = ' + cast( @i as varchar(1)) + 21> ' @@fetch_status = ' + cast(@@fetch_status as char) 22> 23> fetch relative -2 from c1 into @i 24> print 'FETCH RELATIVE -2 = ' + cast( @i as varchar(1)) + 25> ' @@fetch_status = ' + cast(@@fetch_status as char) 26> 27> fetch relative 3 from c1 into @i 28> print 'FETCH RELATIVE 3 = ' + cast( @i as varchar(1)) + 29> ' @@fetch_status = ' + cast(@@fetch_status as char) 30> 31> fetch relative 100 from c1 into @i 32> print 'FETCH RELATIVE 100 = ' + cast( @i as varchar(1)) + 33> ' @@fetch_status = ' + cast(@@fetch_status as char) 34> 35> fetch relative -1 from c1 into @i 36> print 'FETCH RELATIVE -1 = ' + cast( @i as varchar(1)) + 37> ' @@fetch_status = ' + cast(@@fetch_status as char) 38> 39> 40> close c1 41> deallocate c1 42> 43> 44> 45> go FETCH RELATIVE ======================================= FETCH RELATIVE 1 = 0 @@fetch_status = 0 FETCH RELATIVE -1 = 0 @@fetch_status = -1 FETCH RELATIVE -2 = 0 @@fetch_status = -1 FETCH RELATIVE 3 = 2 @@fetch_status = 0 FETCH RELATIVE 100 = 2 @@fetch_status = -1 FETCH RELATIVE -1 = 9 @@fetch_status = 0 1>