Professional Documents
Culture Documents
What is a cursor? Cursor is a variable in SQL Server Database which is used for row-by row operations. The cursor is so named because it indicates the current position in the resultset.
Here I am just taking 2 columns from a table and passing through the cursor and printing them. CREATE PROCEDURE Usp_cursor_test AS BEGIN Declaring the variables needed for cursor to store data DECLARE @Name VARCHAR(50) DECLARE @EmptypeID INT Declaring the Cursor cur_print For name and Emptypeid in the Employeedetails table DECLARE cur_print CURSOR FOR SELECT name, emptypeid FROM employee.employeedetails After declaring we have to open the cursor OPEN cur_print retreives the First row from cursor and storing it into the variables. FETCH NEXT FROM cur_print INTO @Name, @EmptypeID @@FETCH_STATUS returns the status of the last cursor FETCH statement issued against any cursor currently opened by the connection. @@FETCH_STATUS = 0 means The FETCH statement was successful.
@FETCH_STATUS = -1 The FETCH statement failed or the row was beyond the result set. @@FETCH_STATUS = -2 The row fetched is missing. WHILE @@FETCH_STATUS = 0 BEGIN Operations need to be done,Here just printing the variables PRINT @Name PRINT @EmptypeID retreives the NExt row from cursor and storing it into the variables.z
FETCH NEXT FROM cur_print INTO @Name, @EmptypeID END Closing the cursor CLOSE cur_print removes the cursor reference and relase cursor from memory very Important DEALLOCATE cur_print END Note:
Once cursor is opened we have to close the cursor After the usage cursor should be deallocated from the memory. As a DBA , I will not recommend the usage of cursors in all scenarios because it affects performance, since for each result it will have a network round trip which will cause a major performance issue in large data sets. You can make use of case statement instead of cursors for some scenarios.
nested cursor
DECLARE @EntityId Varchar(16)
DECLARE outerCursor CURSOR FOR SELECT EntityId, BaseId FROM outerTable --Returns 204,000 rows
WHILE @@FETCH_STATUS = 0 BEGIN DECLARE innerCursor CURSOR FOR SELECT PRFMR_ID FROM innerTable WHERE ENTY_ID = @BaseId
WHILE @UpdateStatus = 0 BEGIN UPDATE 200MilRowTable SET ENTY_ID = @EntityId WHERE PRFMR_ID = @PerfId
FETCH NEXT FROM innerCursor INTO @PerfId SET @UpdateStatus = @@FETCH_STATUS END
--backup
@name VARCHAR(50) -- database name @path VARCHAR(256) -- path for backup files @fileName VARCHAR(256) -- filename for backup @fileDate VARCHAR(20) -- used for file name
SET @path = 'C:\Backup\' SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) DECLARE db_cursor CURSOR FOR SELECT name FROM MASTER.dbo.sysdatabases WHERE name NOT IN ('master','model','msdb','tempdb') OPEN db_cursor FETCH NEXT FROM db_cursor INTO @name WHILE @@FETCH_STATUS = 0 BEGIN SET @fileName = @path + @name + '_' + @fileDate + '.BAK' BACKUP DATABASE @name TO DISK = @fileName FETCH NEXT FROM db_cursor INTO @name END CLOSE db_cursor DEALLOCATE db_cursor
http://www.sqlteam.com/ http://sqlusa.com/bestpractices2005/doublecursor/
2. Open
A Cursor is opened and populated by executing the SQL statement defined by the cursor.
3. Fetch
When cursor is opened, rows can be fetched from the cursor one by one or in a block to do data manipulation.
4. Close
After data manipulation, we should close the cursor explicitly.
5. Deallocate
Finally, we need to delete the cursor definition and released all the system resources associated with the cursor.
Syntax to Declare Cursor
Declare Cursor SQL Comaand is used to define the cursor with many options that impact the scalablity and loading behaviour of the cursor. The basic syntax is given below
DECLARE cursor_name CURSOR [LOCAL | GLOBAL] --define cursor scope [FORWARD_ONLY | SCROLL] --define cursor movements (forward/backward) [STATIC | KEYSET | DYNAMIC | FAST_FORWARD] --basic type of cursor [READ_ONLY | SCROLL_LOCKS | OPTIMISTIC] --define locks FOR select_statement --define SQL Select statement FOR UPDATE [col1,col2,...coln] --define columns that need to be updated
A Cursor can be opened locally or globally. By default it is opened locally. The basic syntax to open cursor is given below:
OPEN [GLOBAL] cursor_name --by default it is local
Fetch statement provides the many options to retrieve the rows from the cursor. NEXT is the default option. The basic syntax to fetch cursor is given below:
FETCH [NEXT|PRIOR|FIRST|LAST|ABSOLUTE n|RELATIVE n] FROM [GLOBAL] cursor_name INTO @Variable_name[1,2,..n]
Close statement closed the cursor explicitly. The basic syntax to close cursor is given below:
Deallocate statement delete the cursor definition and free all the system resources associated with the cursor. The basic syntax to close cursor is given below:
DEALLOCATE cursor_name --after deallocation it can't be reopen
SET NOCOUNT ON DECLARE @Id int DECLARE @name varchar(50) DECLARE @salary int DECLARE cur_emp CURSOR STATIC FOR SELECT EmpID,EmpName,Salary from Employee OPEN cur_emp IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM cur_emp INTO @Id,@name,@salary WHILE @@Fetch_status = 0 BEGIN PRINT 'ID : '+ convert(varchar(20),@Id)+', Name : '+@name+ ', Salary : '+convert(varchar(20),@salary) FETCH NEXT FROM cur_emp INTO @Id,@name,@salary
2. Dynamic Cursors
A dynamic cursor allows you to see the data updation, deletion and insertion in the data source while the cursor is open. Hence a dynamic cursor is sensitive to any changes to the data source and supports update, delete operations. By default dynamic cursors are scrollable.
IF @name='Mohan' Update Employee SET Salary=15000 WHERE CURRENT OF Dynamic_cur_empupdate FETCH NEXT FROM Dynamic_cur_empupdate INTO @Id,@name END END CLOSE Dynamic_cur_empupdate DEALLOCATE Dynamic_cur_empupdate SET NOCOUNT OFF Go Select * from Employee
-- Dynamic Cursor for DELETE SET NOCOUNT ON DECLARE @Id int DECLARE @name varchar(50) DECLARE Dynamic_cur_empdelete CURSOR DYNAMIC FOR SELECT EmpID,EmpName from Employee ORDER BY EmpName OPEN Dynamic_cur_empdelete IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM Dynamic_cur_empdelete INTO @Id,@name WHILE @@Fetch_status = 0 BEGIN IF @name='Deepak' DELETE Employee WHERE CURRENT OF Dynamic_cur_empdelete FETCH NEXT FROM Dynamic_cur_empdelete INTO @Id,@name END END CLOSE Dynamic_cur_empdelete DEALLOCATE Dynamic_cur_empdelete SET NOCOUNT OFF Go Select * from Employee
DECLARE @name varchar(50) DECLARE Forward_cur_empupdate CURSOR FORWARD_ONLY FOR SELECT EmpID,EmpName from Employee ORDER BY EmpName OPEN Forward_cur_empupdate IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM Forward_cur_empupdate INTO @Id,@name WHILE @@Fetch_status = 0 BEGIN IF @name='Amit' Update Employee SET Salary=24000 WHERE CURRENT OF Forward_cur_empupdate FETCH NEXT FROM Forward_cur_empupdate INTO @Id,@name END END CLOSE Forward_cur_empupdate DEALLOCATE Forward_cur_empupdate SET NOCOUNT OFF Go Select * from Employee
-- Forward Only Cursor for Delete SET NOCOUNT ON DECLARE @Id int DECLARE @name varchar(50) DECLARE Forward_cur_empdelete CURSOR FORWARD_ONLY FOR SELECT EmpID,EmpName from Employee ORDER BY EmpName OPEN Forward_cur_empdelete IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM Forward_cur_empdelete INTO @Id,@name WHILE @@Fetch_status = 0 BEGIN IF @name='Sonu' DELETE Employee WHERE CURRENT OF Forward_cur_empdelete FETCH NEXT FROM Forward_cur_empdelete INTO @Id,@name END END CLOSE Forward_cur_empdelete DEALLOCATE Forward_cur_empdelete SET NOCOUNT OFF Go Select * from Employee
-- Keyse Driven Cursor for Delete SET NOCOUNT ON DECLARE @Id int DECLARE @name varchar(50) DECLARE Keyset_cur_empdelete CURSOR KEYSET FOR SELECT EmpID,EmpName from Employee ORDER BY EmpName OPEN Keyset_cur_empdelete IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM Keyset_cur_empdelete INTO @Id,@name WHILE @@Fetch_status = 0 BEGIN IF @name='Amit' DELETE Employee WHERE CURRENT OF Keyset_cur_empdelete
FETCH NEXT FROM Keyset_cur_empdelete INTO @Id,@name END END CLOSE Keyset_cur_empdelete DEALLOCATE Keyset_cur_empdelete SET NOCOUNT OFF Go Select * from Employee
Keeping in mind the window-washer analogy, let's walk through the steps you would take in using a cursor. Two sets of syntaxes are supported by cursors: SQL-92 and T-SQL Extended Syntax. For the most part I will look at the T-SQL Extended Syntax and reference the SQL-92 syntax for comparative purposes. There are no new cursor features in SQL Server 2005. First you create the cursor using a declare statement, which involves setting the cursor options and specifying the results set. Cursor options There are four sets of cursor options: STATIC The STATIC cursor copies the data in the results set into tempdb and DML that occurs in the underlying results fails to reflect in the cursor's data. Subsequent fetch statements are made on the results set in tempdb. This is perfect when the data underlying your cursor is static or your cursor has no real-time requirements. For example, most cursors Microsoft uses are declared as static as the operation being carried out on the data needs to be done on point-in-time requirements. In other words, it does not need to know about new rows -- the data is static.
KEYSET The KEYSET cursor is implemented by copying primary key data into tempdb. As the cursor moves through the result set, the cursor will see modifications to the underlying data but it will not see new rows. If data is fetched from a row that no longer exists, nulls will be returned and the @@FETCH_STATUS variable will have a value of -2. The order of the cursor data is also maintained. The KEYSET cursor is the default cursor type. Fetch statements are made on the underlying base tables based on the keyset data cached in tempdb. These cursors take more time to open than DYNAMIC cursors, but they also have fewer resource requirements. DYNAMIC The DYNAMIC cursor is similar to the KEYSET cursor in that the cursor can see data modifications in the underlying base tables, but it can also see newly inserted and deleted rows. It does not preserve order, which can lead to the Halloween problem as illustrated in script 4. Fetch statements are made on the underlying base tables, based on the key data cached in tempdb, but the key data is refreshed with each modification to key data on the base tables. It is the most expensive cursor type to implement. FAST_FORWARD A FAST_FORWARD cursor provides optimal performance but only supports the NEXT argument, which only fetches the next row. Other cursor types will be able to fetch the next row, the prior row (using the PRIOR command), the first row (using the FIRST argument), the last row using the LAST argument, the nth row (using the ABSOLUTE arguments), or leap ahead n rows from the current cursor location (using the RELATIVE argument). The above cursor options control the following: Scope or visibility Is the cursor only visible within a batch (local) or beyond the batch (global)? Cursors are only visible within a connection. Scrollability Can the fetch statement fetch only the next row, fetch in any direction and/or by a specific number of rows? The advantage of a forward-only cursor is that it performs faster than a cursor type that can move in any direction. The two options are FORWARD_ONLY and SCROLL (any number and in any direction). Membership Which rows are members of your cursor? Can the cursor see changes happening in the underlying results set, and can it see newly inserted/deleted rows? Updatability Can you update or delete the rows in the underlying results set? To update the tables underlying your cursor, you must have the following: 1. A primary key on the base tables underlying your cursor to update them. Otherwise you will get the message:
Server: Msg 16929, Level 16, State 1, Line 1 The cursor is READ ONLY. The statement has been terminated. 2. A cursor defined as KEYSET, DYNAMIC or FAST_FORWARD. 3. The WHERE CURRENT syntax to update or delete a row. Please refer to this script for an illustration of cursor updatability functions. You retrieve rows from the cursor using fetch statements. You should always check the value of the @@Fetch_Status variable to ensure that it has a value of 0. The @@Fetch_Status variable can have three values:
0 row -1- fetch statement has read beyond -2 - row no longer exists in your results set successfully number of rows returned the cursor
the
in
The fetch statements are analogous to our window washers moving down the sides of the sky scraper. With the fetch statement, the logical operations are position and then retrieve; twice as many operations as a set statement (i.e., with a set statement it's INSERT, UPDATE or DELETE). Finally, clean up after your cursor using the close MyCursorName statement and then deallocate its resources using the deallocation MyCursorName statement. Note that a quick way to return to the beginning of a FAST_FORWARD cursor is to close and reopen it.
xecute the following Microsoft SQL Server T-SQL example scripts in Management Studio Query Editor to demonstrate the construction of cursor and nested cursors logic.
SQL Server Cursor Performance: Cursor solutions do not scale well for large data sets. For such cases, it is better to use set-based operations. See setbased solution T-SQL script following the Purchase Order nested cursors demo.
-- SQL Server cursor example - row-by-row operation - DECLARE CURSOR DECLARE @dbName sysname DECLARE AllDBCursor CURSOR SELECT WHERE name FROM STATIC LOCAL FOR
MASTER.dbo.sysdatabases
WHILE (@@FETCH_STATUS = 0) -- loop through all db-s BEGIN /***** PROCESSING (like BACKUP) db by db goes here - record-by-record process *****/ PRINT @dbName FETCH AllDBCursor INTO @dbName
END -- while CLOSE AllDBCursor; DEALLOCATE AllDBCursor; /* Messages AdventureWorks AdventureWorks2008 AdventureWorksDW AdventureWorksDW2008 ..... */ ------------- T-SQL Cursor declaration and usage example - cursor loop syntax - using tsql cursor -----------USE AdventureWorks2008; DECLARE curSubcategory CURSOR STATIC LOCAL cursor FOR SELECT ProductSubcategoryID, Subcategory=Name FROM Production.ProductSubcategory ORDER BY Subcategory DECLARE @Subcategory varchar(40), @PSID int OPEN curSubcategory FETCH NEXT FROM curSubcategory INTO @PSID, @Subcategory -- sql fetch cursor -- sql declare
/***** USER DEFINED CODE HERE - POSSIBLY NESTED CURSOR *****/ DECLARE @Msg varchar(128) SELECT @Msg = 'ProductSubcategory info: ' + @Subcategory + ' '+ CONVERT(varchar,@PSID) PRINT @Msg FETCH NEXT FROM curSubcategory INTO @PSID, @Subcategory END -- end cursor loop CLOSE curSubcategory DEALLOCATE curSubcategory GO /* Partial output in Messages -- sql fetch cursor
ProductSubcategory info: Bib-Shorts 18 ProductSubcategory info: Bike Racks 26 ProductSubcategory info: Bike Stands 27 ProductSubcategory info: Bottles and Cages 28 ProductSubcategory info: Bottom Brackets 5 ProductSubcategory info: Brakes 6 */ ------------
------------- T SQL Search All Text & XML Columns in All Tables ------------
-- SQL nested cursors - sql server nested cursor - transact sql nested cursor USE AdventureWorks; GO -- SQL Server create stored procedure with nested cursors CREATE PROC sprocSearchKeywordInAllTables AS BEGIN SET NOCOUNT DECLARE ON @Keyword NVARCHAR(64)
SET @OutputLength = '256' SET @NolockOption = '' -- SET @NolockOption = DECLARE @DynamicSQL '(NOLOCK)'
NVARCHAR(MAX), NVARCHAR(256),
@SchemaTableName
SET @SearchWildcard = QUOTENAME('%' + @Keyword + '%',CHAR(39)+CHAR(39)) PRINT @SearchWildcard DECLARE @SearchResults TABLE( SchemaTableColumn NVARCHAR(384), TextWithKeyword ) NVARCHAR(MAX)
QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) AS ST INFORMATION_SCHEMA.TABLES TABLE_TYPE = 'BASE TABLE' AND OBJECTPROPERTY(OBJECT_ID(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)), 'IsMSShipped') != 1
WHILE (@@FETCH_STATUS = 0) -- Outer cursor loop BEGIN PRINT @SchemaTableName SET @SchemaTableColumn = '' DECLARE curAllColumns CURSOR SELECT FROM WHERE FOR -- Nested cursor
QUOTENAME(COLUMN_NAME) INFORMATION_SCHEMA.COLUMNS TABLE_NAME = PARSENAME(@SchemaTableName,1) AND TABLE_SCHEMA = PARSENAME(@SchemaTableName,2) AND DATA_TYPE IN ('varchar','nvarchar','char','nchar','xml')
ORDER BY ORDINAL_POSITION OPEN curAllColumns FETCH NEXT FROM curAllColumns INTO @SchemaTableColumn WHILE (@@FETCH_STATUS = 0) -- Inner cursor loop (nested cursor while) BEGIN
PRINT '
' + @SchemaTableColumn
SET @DynamicSQL = 'SELECT ''' + @SchemaTableName + '.' + @SchemaTableColumn + ''', LEFT(CONVERT(nvarchar(max),' + @SchemaTableColumn + '),' + @OutputLength + ') @SchemaTableName + ' '+@NolockOption+ ' WHERE CONVERT(nvarchar(max),' + @SchemaTableColumn + ') LIKE ' + @SearchWildcard INSERT INTO @SearchResults EXEC sp_executeSQL @DynamicSQL FROM ' +
FETCH NEXT FROM curAllColumns INTO @SchemaTableColumn END -- Inner cursor loop
CLOSE curAllColumns DEALLOCATE curAllColumns FETCH NEXT FROM curAllTables INTO @SchemaTableName END -- Outer cursor loop
@SearchResults
'Hamilton' 'Snchez'
'O''Donnell' 'Certification'
The following nested cursors consist of an outer cursor for purchase orders header info and an inner cursor for the details of each purchase order. It is an example for MS SQL nested cursor loop.
------------- SQL Server Nested Cursors example - transact sql nested cursor ------------- SQL nested cursors - transact sql fetch_status - transact sql while loop -- SQL nesting cursors - transact sql fetch next -- T-SQL script for execution timing setup USE AdventureWorks; DBCC DROPCLEANBUFFERS DECLARE @StartTime datetime SET @StartTime = getdate()
-- Setup local variables DECLARE @IterationID INT, @OrderDetail VARCHAR(max), @ProductName VARCHAR(10)
-- Setup table variable DECLARE @Result TABLE (PurchaseOrderID INT, OrderDetail VARCHAR(max))
DECLARE curOrdersForReport CURSOR STATIC LOCAL FOR SELECT PurchaseOrderID FROM Purchasing.PurchaseOrderHeader WHERE Year(OrderDate) = 2004 AND Month(OrderDate) = 2 ORDER BY PurchaseOrderID
OPEN curOrdersForReport FETCH NEXT FROM curOrdersForReport INTO @IterationID PRINT 'OUTER LOOP START'
-- INNER CURSOR declaration - transact sql declare cursor -- SQL Nested Cursor - sql cursor nested - cursor nesting
DECLARE curDetailList CURSOR STATIC LOCAL FOR SELECT p.productNumber FROM Purchasing.PurchaseOrderDetail pd INNER JOIN Production.Product p ON pd.ProductID = p.ProductID WHERE pd.PurchaseOrderID = @IterationID ORDER BY PurchaseOrderDetailID
OPEN curDetailList FETCH NEXT FROM curDetailList INTO @ProductName PRINT 'INNER LOOP START'
WHILE (@@FETCH_STATUS = 0) BEGIN SET @OrderDetail = @OrderDetail + @ProductName + ', ' FETCH NEXT FROM curDetailList INTO @ProductName PRINT 'INNER LOOP' END -- inner while
-- Truncate trailing comma SET @OrderDetail = left(@OrderDetail, len(@OrderDetail)-1) INSERT INTO @Result VALUES (@IterationID, @OrderDetail)
FETCH NEXT FROM curOrdersForReport INTO @IterationID PRINT 'OUTER LOOP' END -- outer while CLOSE curOrdersForReport DEALLOCATE curOrdersForReport
-- Publish results
-- Execution timing setup DBCC DROPCLEANBUFFERS DECLARE @StartTime datetime SET @StartTime = getdate()
-- SQL comma-limited list generation -- SQL nested select statement -- SQL FOR XML PATH SELECT poh.PurchaseOrderID, OrderDetail = Stuff(( -- SQL correlated subquery SELECT ', ' + ProductNumber as [text()] FROM Purchasing.PurchaseOrderDetail pod INNER JOIN Production.Product p
ON pod.ProductID = p.ProductID WHERE pod.PurchaseOrderID = poh.PurchaseOrderID ORDER BY PurchaseOrderDetailID FOR XML PATH ('')), 1, 1, '') FROM Purchasing.PurchaseOrderHeader poh WHERE Year(OrderDate) = 2004 AND Month(OrderDate) = 2 ORDER BY PurchaseOrderID ;
/* Partial results
OrderDetail GT-0820, GT-1209 HN-6320, HN-7161, HN-7162, HN-8320, HN-9161, HN-9168 NI-4127 RM-T801 LI-1201, LI-1400, LI-3800 TI-R982, TI-T723
The following example uses @@FETCH_STATUS to control the WHILE loop in a typical cursor application:
-- T-SQL cursor declaration DECLARE curManager CURSOR SELECT EmployeeID, Title FROM WHERE AdventureWorks.HumanResources.Employee Title LIKE '%manager%' OR Title LIKE '%super%'; FOR
OPEN curManager; FETCH NEXT FROM curManager; WHILE @@FETCH_STATUS = 0 BEGIN PRINT 'Cursor loop' FETCH NEXT FROM curManager; END; -- while CLOSE curManager; DEALLOCATE curManager; GO /* Partial results
EmployeeID 3
EmployeeID
Title
6 */
Marketing Manager
However, the @@FETCH_STATUS is global to all cursors on a connection, therefore using @@FETCH_STATUS to control nested cursors may not be advisable. To play it safe for the case of following triple nested cursors demonstration, we avoid using @@FETCH_STATUS. Instead we order the SELECTs for the cursor and find the max value on one unique column. We use a comparison between the running values and maximum value to control the loop. The OUTER cursor loop is based on OrderDate. The MIDDLE cursor loop is based PurchaseOrderID-s received on a particular date. The INNER cursor loop is based on the products belonging to a particular PurchaseOrderID. This is the entire triple nested cursors T-SQL script:
-- MSSQL nested cursors USE AdventureWorks GO DECLARE @DateIteration DATETIME, @IterationID @OrderDetail @ProductNo DECLARE INT, VARCHAR(1024), VARCHAR(10)
DECLARE
@Result
FOR
ORDER BY OrderDate
SELECT @MaxOrderDate = OrderDate FROM WHERE Purchasing.PurchaseOrderHeader year(OrderDate) = 2002 AND month(OrderDate) = 7 OPEN curOrderDate FETCH NEXT FROM curOrderDate INTO @DateIteration PRINT 'OUTER LOOP' WHILE (1 < 2) BEGIN DECLARE curOrdersForReport CURSOR SELECT FROM WHERE PurchaseOrderID Purchasing.PurchaseOrderHeader OrderDate = @DateIteration FOR
ORDER BY PurchaseOrderID
OPEN curOrdersForReport FETCH NEXT FROM curOrdersForReport INTO @IterationID PRINT 'MIDDLE LOOP' WHILE (1 < 2) BEGIN SET @OrderDetail = '' DECLARE curDetailList CURSOR SELECT FROM p.ProductNumber Purchasing.PurchaseOrderDetail pd INNER JOIN Production.Product p ON pd.ProductID = p.ProductID WHERE pd.PurchaseOrderID = @IterationID FOR
ORDER BY p.ProductNumber
SELECT @MaxProdNo = p.ProductNumber FROM Purchasing.PurchaseOrderDetail pd INNER JOIN Production.Product p ON pd.ProductID = p.ProductID WHERE pd.PurchaseOrderID = @IterationID
OPEN curDetailList FETCH NEXT FROM curDetailList INTO @ProductNo PRINT 'INNER LOOP' WHILE (1 < 2)
BEGIN SET @OrderDetail = @OrderDetail + @ProductNo + ', ' IF (@ProductNo = @MaxProdNo) BREAK FETCH NEXT FROM curDetailList INTO @ProductNo PRINT 'INNER LOOP' END CLOSE curDetailList DEALLOCATE curDetailList
IF (@IterationID = @MaxPOID) BREAK FETCH NEXT FROM curOrdersForReport INTO @IterationID PRINT 'MIDDLE LOOP' END CLOSE curOrdersForReport DEALLOCATE curOrdersForReport IF (@DateIteration = @MaxOrderDate) BREAK FETCH NEXT FROM curOrderDate INTO @DateIteration PRINT 'OUTER LOOP'
SELECT * FROM GO
@Result
/* Messages (partial)
OUTER LOOP MIDDLE LOOP INNER LOOP INNER LOOP INNER LOOP ... */
July 1, 2002 July 1, 2002 July 13, 2002 July 13, 2002 July 13, 2002 July 13, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 24, 2002 July 27, 2002 July 27, 2002 July 27, 2002 July 27, 2002 July 27, 2002 July 27, 2002 July 27, 2002 July 27, 2002
171 CR-7833, 172 RA-2345, 173 PB-6109, 174 CR-9981, 175 SD-2342, SD-9872, 176 PA-187B, PA-361R, PA-529S, PA-632U, SE-M236, SE-M798, SE-M940, SE-R581, SE-R908, SE177 R995, 178 RF-9198, 179 FC-3982, FL-2301, RC-0291, 180 RM-M464, RM-M692, 181 TP-0923, 182 FC-3982, FL-2301, 183 RM-M464, RM-M692, 184 NI-9522, FW-1000, FW-1200, FW-1400, FW-3400, FW-3800, FW185 5160, FW-5800, FW-7160, FW-9160, 186 PD-M282, PD-M340, HN-3824, HN-4402, HN-5161, HN-5162, HN-5400, HN187 5811, 188 MS-1981, MS-2259, MS-2341, MS-2348, MS-6061, 189 KW-4091, 190 RM-R436, RM-R600, RM-R800, 191 LE-5160, LE-6000, SE-T312, SE-T762, 192 SH-4562, 193 SH-9312, 194 SE-M236, SE-M798, 195 GT-0820, GT-1209, 196 PD-M282, PD-M340, 197 SD-9872, 198 SE-R581, SE-R908, 199 SE-M940, 200 PD-M562,