I have a question. I am working on cursors. Each time, after fetching the last records and printing its data’s, the cursor prints an addition line. To understand what I mean please consider the following sample example: I want to print the information about only 10 customers.
USE Northwind
GO
DECLARE myCursor CURSOR
FOR SELECT TOP(10) ContactName FROM Customers
DECLARE @RowNo int,@ContactName nvarchar(30)
SET @RowNo=1
OPEN myCursor
FETCH NEXT FROM myCursor INTO @ContactName
PRINT LEFT(CAST(@rowNo as varchar) + ' ',6)+' '+ @ContactName
SET @RowNo=@RowNo+1
SET @ContactName=''
WHILE @@FETCH_STATUS=0
BEGIN
FETCH NEXT FROM myCursor INTO @ContactName
PRINT + LEFT(CAST(@rowNo as varchar) + ' ',6)+' '+ @ContactName
SET @RowNo=@RowNo+1
SET @ContactName=''
END
CLOSE myCursor
DEALLOCATE myCursor
Now look at the output:
1 Maria Anders
2 Ana Trujillo
3 Antonio Moreno
4 Thomas Hardy
5 Christina Berglund
6 Hanna Moos
7 Frédérique Citeaux
8 Martín Sommer
9 Laurence Lebihan
10 Elizabeth Lin开发者_如何学JAVAcoln
11
The row number 11 also has been printed. Is it a problem in a cursor or it always occurs? Is there any way not to print this addition data? Thanks (i use sql erver 2008)
Either...
FETCH NEXT FROM myCursor INTO @ContactName
WHILE @@FETCH_STATUS = 0
BEGIN
-- do stuff
FETCH NEXT FROM myCursor INTO @ContactName
END
Or...
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM myCursor INTO @ContactName
IF @@FETCH_STATUS = 0
BEGIN
-- do stuff
END
END
Or...
WHILE (1 = 1)
BEGIN
FETCH NEXT FROM myCursor INTO @ContactName
IF @@FETCH_STATUS <> 0
BREAK
-- do stuff
END
You mentioned you're using SQL Server 2008. With SQL Server 2005 or greater, you don't need a cursor at all to do what you want.
select top 10 left(cast(row_number() over(order by ContactName) as varchar)+ ' ', 6) + ContactName
from Customers
See how you have the printing logic duplicated? That's a pointer to what's going wrong. Your loop should look like this:
FETCH NEXT INTO @working_variables
WHILE @@FETCH_STATUS = 0
-- process @working_variables
FETCH NEXT INTO @working_variables
The only duplicated code should be the FETCH NEXT
itself - the way you have it now, the last FETCH
happens, but you PRINT
a line before the WHILE
can exit.
A FETCH at the end of the record set sets @@FETCH_STATUS to not 0.
The FETCH NEXT command should be the last line in the WHILE BLOCK.
USE Northwind
GO
DECLARE myCursor CURSOR
FOR SELECT TOP(10) ContactName FROM Customers
DECLARE @RowNo int,@ContactName nvarchar(30)
SET @RowNo=0
OPEN myCursor
FETCH NEXT FROM myCursor INTO @ContactName
WHILE @@FETCH_STATUS=0
BEGIN
SET @RowNo=@RowNo+1
SET @ContactName=''
PRINT + LEFT(CAST(@rowNo as varchar) + ' ',6)+' '+ @ContactName
FETCH NEXT FROM myCursor INTO @ContactName
END
CLOSE myCursor
DEALLOCATE myCursor
This is an off-by-one error. Here's a better way to iterate through a cursor, w/ less code duplication:
USE Northwind
GO
DECLARE myCursor CURSOR
FOR SELECT TOP(10) ContactName FROM Customers
DECLARE @RowNo int,@ContactName nvarchar(30)
SET @RowNo=0 -- initialize counters at zero, increment after the fetch/break
OPEN myCursor
WHILE 1=1 BEGIN -- start an infinite loop
FETCH NEXT FROM myCursor INTO @ContactName
IF @@FETCH_STATUS <> 0 BREAK
SET @RowNo=@RowNo+1
PRINT LEFT(CAST(@rowNo as varchar) + ' ',6)+' '+ @ContactName
END
CLOSE myCursor
DEALLOCATE myCursor
For extra points, use a cursor variable and declare w/ FAST_FORWARD and TYPE_WARNING, or STATIC for small datasets. eg:
DECLARE @cursor CURSOR
SET @cursor = CURSOR FAST_FORWARD TYPE_WARNING FOR
SELECT TOP (10) ContactName FROM Customers
OPEN @cursor
......
CLOSE @cursor
DEALLOCATE @cursor
CLOSE and DEALLOCATE are not strictly necessary, as the cursor variable will go out of scope at the end of the batch. It is still good form, however, as you might add more code at the end later on, and you should free up resources as early as possible.
TYPE_WARNING tells you when SQL Server implicitly converts the requested cursor type (FAST_FORWARD) to another type (typically STATIC), if the requested type is incompatible w/ your SELECT statement.
精彩评论