SQL Server Alternatives to Cursor

Estimated study time: 9 minutes. Rewrite row-by-row logic as a single set-based statement.

Cursors process one row at a time, which rarely scales well. SQL Server's query engine is built around set-based operations, so most cursor logic can be rewritten to run faster - and with less code.

1. Plain Set-Based UPDATE/INSERT

A cursor that loops through rows to update each one individually can usually be replaced with a single UPDATE that applies to every matching row at once.

-- Instead of looping row by row to give a raise:
UPDATE Employees
SET Salary = Salary * 1.10
WHERE Department = 'Sales';

2. Window Functions

Running totals, rankings, and row numbering - classic reasons to reach for a cursor - are built directly into SQL Server via window functions.

SELECT
    EmployeeId,
    Salary,
    SUM(Salary) OVER (ORDER BY EmployeeId) AS RunningTotal,
    RANK() OVER (ORDER BY Salary DESC) AS SalaryRank
FROM Employees;

3. Common Table Expressions (CTEs)

A recursive CTE can replace cursors used to walk hierarchical data, such as an organizational chart or a category tree.

WITH OrgChart AS (
    SELECT EmployeeId, ManagerId, Name, 1 AS Level
    FROM Employees WHERE ManagerId IS NULL

    UNION ALL

    SELECT e.EmployeeId, e.ManagerId, e.Name, o.Level + 1
    FROM Employees e
    JOIN OrgChart o ON e.ManagerId = o.EmployeeId
)
SELECT * FROM OrgChart ORDER BY Level;

4. CROSS APPLY / OUTER APPLY

When a cursor is used to run a per-row lookup (like fetching the latest order for each customer), CROSS APPLY does the same thing set-based.

SELECT c.CustomerId, o.OrderId, o.OrderDate
FROM Customers c
CROSS APPLY (
    SELECT TOP 1 OrderId, OrderDate
    FROM Orders o
    WHERE o.CustomerId = c.CustomerId
    ORDER BY o.OrderDate DESC
) o;

5. WHILE Loop (When You Truly Need Iteration)

If you genuinely need to iterate - say, over a small admin-only list - a WHILE loop on a temp table avoids cursor overhead entirely.

DECLARE @i INT = 1, @max INT;
SELECT @max = COUNT(*) FROM #TempIds;

WHILE @i <= @max
BEGIN
    -- process row @i
    SET @i += 1;
END;
💡 Tip: Before writing a cursor, ask what the loop is actually trying to compute per row - a running value, a rank, a single related row, or a hierarchy. Each of those has a dedicated set-based tool.

Ready to go beyond the basics?

Get hands-on training, live mentorship, and placement support with Uncodemy's Data Analytics Course.

Explore Data Analytics Course →