top of page

Looping In T-SQL

Looping In T-SQL By Mike Bennyhoff

In T-SQL, there is no specific "for each" statement, but there are several ways to iterate over a set of rows in a table or result set. Here are some common techniques:

Looping In T-SQL With A Cursor

In T-SQL, a cursor is a database object used to retrieve and manipulate data row-by-row, instead of processing the entire result set inside the loop at once. Cursors provide a mechanism to iterate over the results of a query and perform operations on each row individually.

The basic syntax for using a cursor in T-SQL involves the following steps:

Declare a cursor and define the syntax of the SQL query that will be used to fetch rows from the database.

Open the cursor to start fetching rows.

Fetch the next row from the cursor.

Process the row data and perform any necessary operations.

Repeat steps 3-4 until all rows have been processed.

Close the cursor and release any associated resources.

Here's an example of using a cursor in T-SQL:

DECLARE @CustomerId INT
DECLARE @CustomerName VARCHAR(50)

DECLARE customer_cursor CURSOR FOR
SELECT CustomerId, CustomerName FROM Customers

OPEN customer_cursor

FETCH NEXT FROM customer_cursor INTO @CustomerId, @CustomerName

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT 'Processing customer: ' + @CustomerName
    
    -- Perform some operation on the current row
    -- For example, update the customer's record
    UPDATE Customers SET IsActive = 1 WHERE CustomerId = @CustomerId
    
    FETCH NEXT FROM customer_cursor INTO @CustomerId, @CustomerName
END

CLOSE customer_cursor
DEALLOCATE customer_cursor

In this following example below, we declare a cursor called customer_cursor that retrieves the CustomerId and CustomerName fields from the Customers table. We then open the cursor and fetch the first row of data into two variables: @CustomerId and @CustomerName.

We then enter a WHILE loop and use the @@FETCH_STATUS function to check whether there are any more rows to fetch. If there are, we print a message indicating that we're processing new iteration for the current customer, and then perform some operation on the row (in this case, we're updating the customer's IsActive flag to 1).

We then begin to fetch the next row of data into the variables, and repeat the process until all rows have been processed. Finally, we close the cursor and deallocate it to release any associated resources.

Cursors in T-SQL can be a useful tool in some cases, but they also have some advantages and disadvantages to consider.

Advantages of Cursors:

  • Flexibility: Cursors are a powerful tool that allows you to fetch and process data row by row, giving you a lot of control over the data processing.

  • Customizable: With cursors, you can define complex processing logic for each row, which can be very useful in some scenarios where a set-based approach is not feasible.

  • Record navigation: Cursors allow you to navigate through a result set one record at a time, making it easier to update or delete specific rows as needed.

Disadvantages of Cursors:

  • Performance: Cursors can be slower than set-based operations, especially when processing large amounts of data. This is because cursors require multiple trips to the database, which can increase network traffic and processing time.

  • Resource usage: Cursors require more memory and processing power compared to set-based operations, which can lead to performance issues on servers with limited resources.

  • Locking: Cursors can cause locking issues, especially when used in transactions, which can result in deadlocks and performance problems.

  • Complexity: Cursors can be complex to use and maintain, especially when nested, which can make the code harder to read and debug.

In general, it's recommended to use cursors only when there is no other alternative. Whenever possible, try to use set-based operations instead, as they are generally faster and more efficient. If cursors are used, it's important to optimize the code as much as possible and to close and deallocate them properly after use to avoid resource issues.

How To Avoid Locking With Cursors

Cursors in T-SQL can cause locking issues, especially when used in transactions, which can result in deadlocks and performance problems. To avoid locking with cursors, you can follow these best practices:

Use the correct cursor type:

There are different types of cursors in T-SQL, and each type behaves differently when it comes to locking. By default, cursors use optimistic concurrency, which means they don't acquire locks on the underlying data. However, if you need to update or delete data using a cursor, you need to use a different cursor type, such as STATIC, KEYSET, or SCROLL, which allow you to use pessimistic concurrency and acquire locks on the underlying data.

Limit the scope of the cursor:

 Cursors should be used only when necessary and for a limited scope. Avoid using cursors in long-running transactions or in nested loops, as this can cause excessive locking and reduce performance.

Use the correct transaction isolation level: The transaction isolation level determines how much locking is used to maintain data consistency. By default, SQL Server uses the READ COMMITTED isolation level, which can cause locking issues when using cursors. To avoid this, you can use a lower isolation level, such as READ UNCOMMITTED or SNAPSHOT, which allow for less locking and better concurrency.

Use the NOLOCK hint:

If you don't need to lock the data being accessed by using a while loop or cursor, you can use the NOLOCK hint to tell SQL Server to use a non-locking read. This can improve concurrency and reduce locking issues, but it also means you might be reading uncommitted data. Optimize the cursor query: Cursors can be slow, especially when processing large amounts of data. To improve performance and reduce locking issues, optimize the cursor query by limiting the number of rows returned, using indexes, and avoiding complex joins or subqueries.

Cursor Types

You would use STATIC, KEYSET, or SCROLL cursors in T-SQL when you need to update or delete data using a cursor, above example, as these cursor types allow you to use pessimistic concurrency and acquire locks on the underlying data. Here are examples of code for each type:

STATIC Cursor: A STATIC cursor is the fastest and least resource-intensive type of cursor, as it creates a temporary copy of the data to be processed and does not allow for any changes to the underlying block of data. This type of cursor is useful when you only need to read data and don't need to update or delete any records.

KEYSET cursor is similar to a STATIC cursor, but it creates a temporary copy of the primary key values for the records to be processed. This allows you to navigate through the records using the primary key values, but also allows you to update or delete records using the cursor.

Here's an example:

-- Declare and open the keyset cursor
DECLARE KeysetCursor CURSOR KEYSET FOR
SELECT EmployeeID, FirstName, LastName, Department
FROM #Employee;

OPEN KeysetCursor;

-- Fetch the first row from the cursor
FETCH NEXT FROM KeysetCursor INTO @EmployeeID, @FirstName, @LastName, @Department;

-- Loop through the cursor and print each row
WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT 'EmployeeID: ' + CAST(@EmployeeID AS VARCHAR(10)) + ', ' +
          'Name: ' + @FirstName + ' ' + @LastName + ', ' +
          'Department: ' + @Department;
          
    -- Fetch the next row from the cursor
    FETCH NEXT FROM KeysetCursor INTO @EmployeeID, @FirstName, @LastName, @Department;
END

-- Close and deallocate the cursor
CLOSE KeysetCursor;
DEALLOCATE KeysetCursor;

-- Drop the temporary table
DROP TABLE #Employee;

SCROLL Cursor: A SCROLL cursor is the most flexible type of cursor, as it allows you to navigate through the records in any direction and allows you to update or delete records using the cursor. This type of cursor is useful when you need to process data in a non-linear way, such as processing data based on user input.

Here's an example:

-- Declare variables to store cursor data
DECLARE @EmployeeID INT,
        @FirstName VARCHAR(50),
        @LastName VARCHAR(50),
        @Department VARCHAR(50);

-- Declare and open the scroll cursor
DECLARE ScrollCursor CURSOR SCROLL FOR
SELECT EmployeeID, FirstName, LastName, Department
FROM #Employee
ORDER BY EmployeeID; -- Ordering by EmployeeID for deterministic scrolling

OPEN ScrollCursor;

-- Fetch the first row from the cursor
FETCH FIRST FROM ScrollCursor INTO @EmployeeID, @FirstName, @LastName, @Department;

-- Loop through the cursor and print each row
WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT 'EmployeeID: ' + CAST(@EmployeeID AS VARCHAR(10)) + ', ' +
          'Name: ' + @FirstName + ' ' + @LastName + ', ' +
          'Department: ' + @Department;
          
    -- Fetch the next row from the cursor
    FETCH NEXT FROM ScrollCursor INTO @EmployeeID, @FirstName, @LastName, @Department;
END

-- Close and deallocate the cursor
CLOSE ScrollCursor;
DEALLOCATE ScrollCursor;

-- Drop the temporary table
DROP TABLE #Employee;

Note that when using any of these cursor types, you need to use the CLOSE and DEALLOCATE statements to release the cursor resources when you're done using it. Also, it's important to optimize the cursor query to limit the number of rows returned and avoid complex joins or subqueries to improve performance.

Looping With The WHILE loop:

In T-SQL, a WHILE loop is a control flow statement that allows you to repeatedly execute a block of code as long as a condition is true. The syntax for a WHILE loop statement is as follows:

WHILE condition
BEGIN
    -- Statements to execute
END

The condition is any expression that evaluates to a Boolean value (TRUE or FALSE). The statements inside the BEGIN and END block of specified condition are executed repeatedly as long as the boolean expression of the condition remains TRUE.

Here's an example of a WHILE for loop example in T-SQL:

DECLARE @counter INT = 1

WHILE @counter <= 10
BEGIN
    PRINT 'Counter value is: ' + CAST(@counter AS VARCHAR(2))
    SET @counter = @counter + 1
END

In this following example below, we declare a variable called @counter and set the variable its initial value to 1. We then use a WHILE loop to repeatedly print out the value of the counter variable and increment it by 1, until it reaches a value of 10.

Video



Get in Touch

Thanks for submitting!

bottom of page