SQL Server Cursor Example

Problem

A SQL Server cursor is a set of T-SQL logic that loops over a predetermined number of rows one at a time. The purpose of the cursor may be to update one row at a time or perform an administrative process, such as SQL Server database backups, in a sequential manner. Development, DBA, and ETL processes rely on SQL Server cursors. Can you provide a SQL cursor example? Can you give any guidance on when to use a SQL Server cursor?

Solution

Cursor in SQL Server

Let’s break down the SQL Server cursor example in this SQL tutorial and notate which sections need to be updated when using this code.

-- 1 - Declare Variables
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
DECLARE @name VARCHAR(50) -- database name 
DECLARE @path VARCHAR(256) -- path for backup files 
DECLARE @fileName VARCHAR(256) -- filename for backup 
DECLARE @fileDate VARCHAR(20) -- used for file name 
 
-- Initialize Variables
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) 
 
-- 2 - Declare Cursor
DECLARE db_cursor CURSOR FOR 
-- Populate the cursor with your logic
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
SELECT name 
FROM MASTER.dbo.sysdatabases 
WHERE name NOT IN ('tempdb') 
 
-- Open Cursor
OPEN db_cursor
 
-- 3 - Fetch the next record from the cursor
FETCH NEXT FROM db_cursor INTO @name  
 
-- Set the status for the cursor
WHILE @@FETCH_STATUS = 0  
 
BEGIN  
-- 4 - Begin the custom business logic
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
   SET @fileName = 'C:\Backup\' + @name + '_' + @fileDate + '.BAK' 
  BACKUP DATABASE @name TO DISK = @fileName 
 
-- 5 - Fetch the next record from the cursor
 FETCH NEXT FROM db_cursor INTO @name 
END 
 
-- 6 - Close cursor
CLOSE db_cursor  
 
-- 7 - Deallocate cursor
DEALLOCATE db_cursor 

Explanation of Cursor Syntax in SQL Server

Based on the SQL cursor example above, cursors include these components:

DECLARE Statements

Declare variables used in the code block such as file names, database names, account numbers, etc. Specify the variable name and data type.

  • Update the logic based on your needs

SET\SELECT Statements

Initialize the variables to a specific value.

DECLARE CURSOR Statement

Declare the cursor with a specific name (i.e., db_cursor in this SQL Tutorial) that you will use throughout the logic along with the business logic (SELECT SQL statement) to populate the records.

  • NOTE – There are an equal number of variables in the DECLARE CURSOR FOR statement as there are in the SELECT statement. This could be one or many variables and associated columns.
  • There are many options and types of cursors, such as:
    • Read_Only
    • Fast_Forward
    • Forward_Only
    • Scroll
    • Static Cursors
    • Dynamic
    • Local
    • Global
    • Keyset
    • Scroll_Locks
    • Optimistic
  • Update the logic based on your needs

OPEN Statement

Open the cursor to begin data processing.

FETCH Statements

Assign the specific values from the cursor to the variables to match the DECLARE CURSOR FOR and SELECT statement.

  • NOTE – This logic is used for the initial population before the WHILE statement and then again during each loop in the process as a portion of the WHILE statement.

WHILE Statement

Condition to begin and continue data processing.

BEGIN…END statement

Start and end of the code block.

  • NOTE – Based on the data processing, multiple BEGIN…END statements can be used.

Data Processing

In this example, this logic is to back up a database to a specific path and file name, but this could be any DML or administrative logic.

  • Update the logic based on your needs

CLOSE Statement

Releases the current data and associated locks but permits the cursor to be re-opened.

DEALLOCATE Cursor Statement

Destroys the cursor.

Why Use a Cursor in SQL Server?

Although using an INSERT, UPDATE, or DELETE statement to modify all of the applicable data in one transaction is generally the best way to work with data in SQL Server, a cursor may be needed for:

  • Iterating over data one row at a time
  • Completing a process in a serial manner, such as SQL Server database backups
  • Updating data across numerous tables for a specific account
  • Correcting data with a predefined set of data as the input to the cursor

When to Use a SQL Server Cursor

The analysis below is intended to serve as insight into various scenarios where cursor-based logic may or may not be beneficial:

Online Transaction Processing (OLTP)

In most OLTP environments, SET based logic (INSERT, UPDATE, or DELETE on applicable rows) makes the most sense for short transactions. Our team has run into a third-party application that uses cursors for all its processing, which has caused issues, but this has been a rare occurrence. Typically, SET based logic is more than feasible, and cursors are rarely needed.

Reporting

Based on the design of the reports and the underlying design, cursors are typically not needed. However, our team has seen reporting requirements where referential integrity does not exist on the underlying database, and it is necessary to use a cursor to correctly calculate the reporting values. We have had the same experience when needing to aggregate data for downstream processes. A cursor-based approach was quick to develop and performed in an acceptable manner to meet the need.

Serialized processing

If you need to complete a process in a serialized manner, cursors are a viable option.

Administrative tasks

Many administrative tasks, such as database backups or Database Consistency Checks, need to be executed in a serial manner, which fits nicely into cursor-based logic. But, other system-based objects exist to fulfill the need. In some of those circumstances, cursors are used to complete the process.

Large data sets

With large data sets you could run into one or more of the following:

  • Cursor based logic may not scale to meet the processing needs.
  • With large set-based operations on servers with a minimal amount of memory, the data may be paged or monopolize the SQL Server, which is time-consuming and can cause contention and memory issues. As such, a cursor-based approach may meet the need.
  • Some tools inherently cache the data to a file under the covers, so processing the data in memory may or may not actually be the case.
  • If the data can be processed in a staging SQL Server database, the impacts to the production environment only occur when the final data is processed. All resources on the staging server can be used for the ETL processes then the final data can be imported.
  • SSIS supports batching sets of data, which may resolve the overall need to break up a large data set into more manageable sizes and perform better than a row-by-row approach with a cursor.
  • Depending on how the cursor or SSIS logic is coded, it may be possible to restart at the point of failure based on a checkpoint or marking each row with the cursor. However, with a set-based approach, that may not be the case until an entire set of data is completed. As such, troubleshooting the row with the problem may be more difficult.

SQL Server Cursor Types

Simon Liew has written a detailed technical tip on five Different ways to Write a Cursor (read here), which includes the following:

  • Most Common SQL Server Cursor Syntax
  • SQL Server Cursor as a Variable
  • SQL Server Cursor as Output of a Stored Procedure
  • SQL Server Cursor Current of Example
  • SQL Server Cursor Alternative with a WHILE Loop

This tip provides sample code that can be used to expand SQL Server cursor options beyond the syntax in this tip.

SQL Server Cursor Alternatives

Learn more about SQL Server cursors and alternatives:

How to Avoid Cursors in SQL Server

Below outlines SQL Server T-SQL coding options to avoid SQL Server cursors:

Difference Between While Loop and Cursor in SQL Server

Daniel Farina wrote an interesting article (SQL Server Loop through Table Rows without Cursor) comparing the SQL Server While Loop and Cursors. He covers the following:

  • Using a While Loop Instead of Cursors in SQL Server
  • Pros and Cons of Using Cursors to Iterate Through Table Rows in SQL Server
  • Pros and Cons of Using a While Loop to Iterate Through Table Rows in SQL Server
  • Example of a Basic Cursor to Loop through Table Rows in SQL Server
  • Example of a Basic While Loop to Cycle through Table Rows in SQL Server

The code samples in this tip are valuable to illustrate the differences between cursors in SQL Server and the While Loop.

SQL Cursor Example Resources

In the example above, backups are issued via a cursor. Check out these other tips that leverage cursor-based logic:

SQL Server Cursor Final Thoughts and Opinion

When you are faced with a data processing decision, determine where you stand with SQL Server cursor usage. They may or may not have a place in your application or operational processes. There are many ways to complete a task. Using a cursor could be a reasonable alternative or not. You be the judge.

If you run into issues with another coding technique and need to get something done quickly, using a cursor may be a viable alternative. It may take longer to process the data, but the coding time might be much less. If you have a one-time process or nightly processing, this could do the trick.

If cursors are shunned in your environment, be sure to select another viable alternative. Be sure the process will not cause other issues. As an example, if a cursor is used and millions of rows are processed, will this potentially flush all of the data from cache and cause further contention? Or, with a large data set, will the data be paged to disk or written to a temporary directory?

Key Takeaways

  • A SQL cursor allows sequential processing of database rows one at a time, useful for tasks like backups.
  • Cursors include key components: DECLARE, SET/SELECT, OPEN, FETCH, WHILE, BEGIN…END, CLOSE, and DEALLOCATE statements.
  • Use cursors for iterating over rows, administrative tasks, or when individual data changes are necessary.
  • Consider alternatives like set-based logic for OLTP transactions or SQL Server Integration Services (SSIS) to improve efficiency for large batch or ETL operations.
  • Evaluate the use of sql cursor against other methods while considering time and resource impacts.

Next Steps

  • As you evaluate a cursor-based approach versus other alternatives, make a fair comparison of the techniques in terms of time, contention, and resources needed. Hopefully, these factors will drive you to the proper technique.
  • Be sure to check out the SQL Quick Reference Guide to improve your development skills.

10 Comments

  1. i just solved my companies call data record reporting in 64 lines using your genius; thank you from the bottom of my heart Jeremy! ^__^

  2. I avoid cursors where possible. E.g. When tidying up input data, I will probably use AWK or if it is in Excel a VBA script.

    However sometimes the mess is so bad that it needs brought into a database and manipulated with a cursor.

    In which case I will probably be fetching multiple columns and doing some debugging.

    The common pattern with Cursors is a double fetch. Fetch first record and then while loop and then fetch next record.
    A nice alternative is to use an UNTIL loop which sadly does not exist in SQL server, however it is easily implemented with a GOTO
    startLabel:
    … fetching
    IF fetched
    …body of loop
    GOTO startLabel — end of UNTIL loop

    This breaks all the rules do not use cursors, do not use GOTO, but it gets rid of the common error if you have code with two fetches where only one gets updated.

  3. Seid and Brock,

    Thank you for the two most recent comments. That makes my day!

    Thank you,
    Jeremy Kadlec
    Community Co-Leader

  4. Great article. Well written. Useful. I get frustrated by some people who like to pontificate on some deep CS philosophy about T-SQL cursors being bad. I just need to loop over records and this works. Thanks again for a great article that is to the point and easy to read.

  5. Nikhil,

    Good morning. I would check out Aaron Bertrand’s tip – https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/. He has a 30 year period in his tip, but you can just change the year parameter as needed.

    HTH.

    Thank you,
    Jeremy Kadlec
    Community Co-Leader

  6. I have to create a calendar starting from jan 2020 and fiscal calendar starting from july 2020 and it should display the data of next 5 years we should pass parameter.using cursors,i am unable to get it.

  7. Moum,

    I hope this tip helped you.

    I would also encourage you to check out these resources:

    https://www.mssqltips.com/sqlservertip/6308/different-ways-to-write-a-cursor-in-sql-server/

    https://www.mssqltips.com/sqlservertip/6148/sql-server-loop-through-table-rows-without-cursor/

    https://www.mssqltips.com/sqlservertip/1414/run-same-command-on-all-sql-server-databases-without-cursors/

    https://www.mssqltips.com/tutorial/sql-server-convert-cursor-to-set-based/

    HTH.

    Thank you,
    Jeremy Kadlec
    Community Co-Leader

  8. Actually, you don’t really need cursors. They exist in SQL because the original SQL implementations were built on top of existing filesystems. SQL was a weak language; we had a joke on the ANSI Standards committee that SQL stood for “Scarcely Qualifies as a Language” while we were working on it.

    If you become a computer science major, you’ll run into two things, and prove their equivalency. The first is finite state automaton and the second is primitive recursive functions. The first one is the abstract mathematical model for what we think of as a computer. There’s usually a discussion about how many push down stacks a machine has and some proofs as to what kind of computations various versions of these abstract machines can compute. Primitive recursive functions are more like SQL; they consist of functions that can be recursively nested inside each other. We need a selection (if - then - else), a loop (while - do) and the concatenation (begin - end) to do the same thing as we could do with our finite state automaton. The proof is really quite nice and boring as hell, something only a real nerd would love. But the formal proof shows that you don’t need cursors.

    Back in the real world, Sybase used to have an example their training manuals that used a bookstore database. They had a sale which would raise the price of cheap books (cost <= $25) without telling the customers and make a big deal about reducing the price of expensive books (cost > $25). The original UPDATE statements in the training manual looked like this:

    BEGIN
    UPDATE Books — expensive books
    SET book_price = book_price * 0.90
    WHERE book_price >= 25.00;
    UPDATE Books — cheap books
    SET book_price = book_price * 1.10
    WHERE book_price < 25.00;
    END;

    The manual then gave some sample data and things look fine until you got to a book whose price was on the cusp. The UPDATE statements would reduce the price, then increase the price. If you switch to the UPDATE statements around, you got a little different version of the same problem. The answer given back in those days was to use a cursor and change the prices row by row.

    Today we have the CASE expression, which corresponds to a basic primitive recursive function called selection, so we can write this as:

    UPDATE Books –all books
    SET book_price =
    CASE WHEN book_price >= 25.00 THEN = book_price * 0.90
    WHEN book_price < 25.00 THEN = book_price * 1.10
    ELSE NULL

    In working with SQL over 30 years, I’ve written five cursors total. Today, I know I could have avoided at least three of them simply with the CASE expression.

Leave a Reply

Your email address will not be published. Required fields are marked *