You are on page 1of 33

cursor

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.

Let us look this example.

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 @PerfId Varchar(16) DECLARE @BaseId Varchar(16) DECLARE @UpdateStatus Int

DECLARE outerCursor CURSOR FOR SELECT EntityId, BaseId FROM outerTable --Returns 204,000 rows

OPEN outerCursor FETCH NEXT FROM outerCursor INTO @EntityId, @BaseId

WHILE @@FETCH_STATUS = 0 BEGIN DECLARE innerCursor CURSOR FOR SELECT PRFMR_ID FROM innerTable WHERE ENTY_ID = @BaseId

OPEN innerCursor FETCH NEXT FROM innerCursor INTO @PerfId

SET @UpdateStatus = @@FETCH_STATUS

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

CLOSE innerCursor DEALLOCATE innerCursor --clean up inner cursor

FETCH NEXT FROM outerCursor INTO @EntityId, @BaseId END

CLOSE outerCursor DEALLOCATE outerCursor cleanup outer cursor

--backup

all SQL Server databases

DECLARE DECLARE DECLARE DECLARE

@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/

SQL Server Basics of Cursors


Cursor is a database objects to retrieve data from a result set one row at a time, instead of the TSQL commands that operate on all the rows in the result set at one time. We use cursor when we need to update records in a database table in singleton fashion means row by row.

Life Cycle of Cursor 1. Declare Cursor


A cursor is declared by defining the SQL statement that returns a result set.

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

Syntax to Open Cursor

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

Syntax to Fetch Cursor

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]

Syntax to Close Cursor

Close statement closed the cursor explicitly. The basic syntax to close cursor is given below:

CLOSE cursor_name --after closing it can be reopen

Syntax to Deallocate Cursor

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

SQL SERVER Simple Examples of Cursors


CREATE TABLE Employee ( EmpID int PRIMARY KEY, EmpName varchar (50) NOT NULL, Salary int NOT NULL, Address varchar (200) NOT NULL, ) GO INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(1,'Mohan',12000,'Noida') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(2,'Pavan',25000,'Delhi') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(3,'Amit',22000,'Dehradun') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(4,'Sonu',22000,'Noida') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(5,'Deepak',28000,'Gurgaon') GO SELECT * FROM Employee

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

END END CLOSE cur_emp DEALLOCATE cur_emp SET NOCOUNT OFF

SQL Server Different Types of Cursors


A Cursor allow us to retrieve data from a result set in singleton fashion means row by row. Cursor are required when we need to update records in a database table one row at a time. I have already explained the basic of cursor. A Cursor impacts the performance of the SQL Server since it uses the SQL Server instances' memory, reduce concurrency, decrease network bandwidth and lock resources. Hence it is mandatory to understand the cursor types and its functions so that you can use suitable cursor according to your needs. You should avoid the use of cursor. Basically you should use cursor alternatives like as WHILE loop, sub queries, Temporary tables and Table variables. We should use cursor in that case when there is no option except cursor.

Types of Cursors 1. Static Cursors


A static cursor populates the result set at the time of cursor creation and query result is cached for the lifetime of the cursor. A static cursor can move forward and backward direction. A static cursor is slower and use more memory in comparison to other cursor. Hence you should use it only if scrolling is required and other types of cursors are not suitable. You can't update, delete data using static cursor. It is not sensitive to any changes to the original data source. By default static cursors are scrollable.

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.

3. Forward Only Cursors


A forward only cursor is the fastest cursor among the all cursors but it doesn't support backward scrolling. You can update, delete data using Forward Only cursor. It is sensitive to any changes to the original data source. There are three more types of Forward Only Cursors.Forward_Only KEYSET, FORWARD_ONLY STATIC and FAST_FORWARD. A FORWARD_ONLY STATIC Cursor is populated at the time of creation and cached the data to the cursor lifetime. It is not sensitive to any changes to the data source. A FAST_FORWARD Cursor is the fastest cursor and it is not sensitive to any changes to the data source.

4. Keyset Driven Cursors


A keyset driven cursor is controlled by a set of unique identifiers as the keys in the keyset. The keyset depends on all the rows that qualified the SELECT statement at the time of cursor was opened. A keyset driven cursor is sensitive to any changes to the data source and supports update, delete operations. By default keyset driven cursors are scrollable.

SQL SERVER Examples of Cursors


CREATE TABLE Employee ( EmpID int PRIMARY KEY, EmpName varchar (50) NOT NULL, Salary int NOT NULL, Address varchar (200) NOT NULL, ) GO INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(1,'Mohan',12000,'Noida') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(2,'Pavan',25000,'Delhi') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(3,'Amit',22000,'Dehradun') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(4,'Sonu',22000,'Noida') INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(5,'Deepak',28000,'Gurgaon') GO SELECT * FROM Employee

Static Cursor - Example


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 END END CLOSE cur_emp DEALLOCATE cur_emp SET NOCOUNT OFF

Dynamic Cursor - Example


--Dynamic Cursor for Update SET NOCOUNT ON DECLARE @Id int DECLARE @name varchar(50) DECLARE Dynamic_cur_empupdate CURSOR DYNAMIC FOR SELECT EmpID,EmpName from Employee ORDER BY EmpName OPEN Dynamic_cur_empupdate IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM Dynamic_cur_empupdate INTO @Id,@name WHILE @@Fetch_status = 0 BEGIN

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

Forward Only Cursor - Example


--Forward Only Cursor for Update SET NOCOUNT ON DECLARE @Id int

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

Keyset Driven Cursor - Example


-- Keyset driven Cursor for Update SET NOCOUNT ON DECLARE @Id int DECLARE @name varchar(50) DECLARE Keyset_cur_empupdate CURSOR KEYSET FOR SELECT EmpID,EmpName from Employee ORDER BY EmpName OPEN Keyset_cur_empupdate IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM Keyset_cur_empupdate INTO @Id,@name WHILE @@Fetch_status = 0 BEGIN IF @name='Pavan' Update Employee SET Salary=27000 WHERE CURRENT OF Keyset_cur_empupdate FETCH NEXT FROM Keyset_cur_empupdate INTO @Id,@name END END CLOSE Keyset_cur_empupdate DEALLOCATE Keyset_cur_empupdate 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

How cursors work


To visualize how a cursor works, think of the apparatus used by a skyscraper window washer to travel up and down the skyscraper, stopping at each floor to wash each window. For most cursor types, key data is brought into memory and the cursor navigates this key data on a row-by-row basis; similar to the window washer going floor by floor. A cursor requires two operations. The placement operation moves the cursor to a row in the results set. The retrieve statement returns the data underlying that row, called a fetch. Set operations accomplish this with a single statement:
select * from TableName where pk=1

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

name NOT IN ('master','tempdb','model','msdb') ORDER BY name

OPEN AllDBCursor; FETCH

AllDBCursor INTO @dbName;

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

WHILE (@@fetch_status = 0) BEGIN -- begin cursor loop

-- sql cursor fetch_status

/***** 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)

@OutputLength VARCHAR(4), @NolockOption CHAR(8)

SET @OutputLength = '256' SET @NolockOption = '' -- SET @NolockOption = DECLARE @DynamicSQL '(NOLOCK)'

NVARCHAR(MAX), NVARCHAR(256),

@SchemaTableName

@SchemaTableColumn NVARCHAR(128), @SearchWildcard NVARCHAR(128)

SET @SearchWildcard = QUOTENAME('%' + @Keyword + '%',CHAR(39)+CHAR(39)) PRINT @SearchWildcard DECLARE @SearchResults TABLE( SchemaTableColumn NVARCHAR(384), TextWithKeyword ) NVARCHAR(MAX)

DECLARE curAllTables CURSOR

STATIC LOCAL FOR

SELECT FROM WHERE

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

ORDER BY ST OPEN curAllTables FETCH NEXT FROM curAllTables INTO @SchemaTableName

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

CLOSE curAllTables DEALLOCATE curAllTables

SELECT DISTINCT SchemaTableColumn, TextWithKeyWord FROM END GO

@SearchResults

EXEC sprocSearchKeywordInAllTables EXEC sprocSearchKeywordInAllTables

'Hamilton' 'Snchez'

EXEC sprocSearchKeywordInAllTables EXEC sprocSearchKeywordInAllTables ------------

'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))

-- OUTER CURSOR declaration - transact sql declare cursor

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'

WHILE (@@FETCH_STATUS = 0) BEGIN SET @OrderDetail = ''

-- sql cursor fetch_status

-- 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

CLOSE curDetailList DEALLOCATE curDetailList

-- 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

SELECT * FROM @Result ORDER BY PurchaseOrderID

-- Timing result SELECT ExecutionMsec = datediff(millisecond, @StartTime, getdate()) GO -- 220 msecs

------------- Equivalent set-based operations solution ------------

-- 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 ;

-- Timing result SELECT ExecutionMsec = datediff(millisecond, @StartTime, getdate()) GO -- 110 msecs

/* Partial results

PurchaseOrderID 1696 1697 1698 1699 1700 1701 */

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

Title Engineering Manager

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)

@MaxOrderDate DATETIME, @MaxPOID @MaxProdNo INT, VARCHAR(10)

DECLARE

@Result

TABLE( OrderDate DATETIME,

PurchaseOrderID INT, OrderDetail ) VARCHAR(1024)

DECLARE curOrderDate CURSOR SELECT FROM WHERE DISTINCT OrderDate

FOR

Purchasing.PurchaseOrderHeader year(OrderDate) = 2002 AND month(OrderDate) = 7

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

SELECT @MaxPOID = PurchaseOrderID FROM WHERE Purchasing.PurchaseOrderHeader OrderDate = @DateIteration

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

INSERT INTO @Result VALUES (@DateIteration,@IterationID,@OrderDetail)

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'

END CLOSE curOrderDate DEALLOCATE curOrderDate

SELECT * FROM GO

@Result

/* Messages (partial)

OUTER LOOP MIDDLE LOOP INNER LOOP INNER LOOP INNER LOOP ... */

Here is the result set:


OrderDate PurchaseOrderID OrderDetail July 1, 2002 157 HJ-3416, HJ-3816, HJ-3824, HJ-5161, HJ-5162, HJ-5811, July 1, 2002 158 BA-8327, July 1, 2002 159 AR-5381, July 1, 2002 160 HJ-3816, HJ-3824, HJ-5161, July 1, 2002 161 SP-2981, July 1, 2002 162 BE-2908, July 1, 2002 163 RM-R800, July 1, 2002 164 RM-T801, July 1, 2002 165 CA-5965, CA-6738, CA-7457, July 1, 2002 166 LI-1201, LI-1400, LI-3800, LI-5160, July 1, 2002 167 LJ-5811, LJ-5818, LJ-7161, LJ-7162, LJ-9080, LJ-9161, July 1, 2002 168 CB-2903, CN-6137, CR-7833, July 1, 2002 169 LN-3410, LN-3416, LN-3816, LN-3824, LN-4400, July 1, 2002 170 PD-T852,

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,

You might also like