SQL Practice Logo

SQLPractice Online

SQL Server: T-SQL Features: Mistakes

Module: Database-Specific Features

Using cursors to update rows one-by-one

Use set-based UPDATE with WHERE clause

Cursors fetch and process rows one-by-one, causing massive overhead. Each row requires: FETCH (network round trip), process (CPU), UPDATE (disk I/O). For 1M rows, that is 1M round trips. Set-based operations process all rows in single statement with optimized execution plan.

Example - Update order totals:

-- WRONG: Cursor approach (10 minutes for 1M rows)

DECLARE @OrderID INT, @Total DECIMAL(10,2);

DECLARE cursor_orders CURSOR FOR

SELECT order_id FROM orders WHERE status = 'pending';

OPEN cursor_orders;

FETCH NEXT FROM cursor_orders INTO @OrderID;

WHILE @@FETCH_STATUS = 0

BEGIN

SELECT @Total = SUM(quantity * unit_price)

FROM order_items WHERE order_id = @OrderID;

UPDATE orders SET total_amount = @Total WHERE order_id = @OrderID;

FETCH NEXT FROM cursor_orders INTO @OrderID;

END;

CLOSE cursor_orders;

DEALLOCATE cursor_orders;

-- CORRECT: Set-based approach (5 seconds for 1M rows)

UPDATE o

SET total_amount = item_totals.total

FROM orders o

JOIN (

SELECT order_id, SUM(quantity * unit_price) AS total

FROM order_items

GROUP BY order_id

) item_totals ON o.order_id = item_totals.order_id

WHERE o.status = 'pending';

Cursors are acceptable only for: 1) Administrative tasks (backup, maintenance), 2) Complex business logic that cannot be expressed in set-based SQL, 3) Processing external API calls per row.

Always try to solve problems with set-based SQL (UPDATE, INSERT, DELETE with WHERE) before considering cursors. If you must use cursors, use FAST_FORWARD option for better performance.

Critical

Cursor processes 1M rows in 10 minutes, set-based UPDATE processes same in 5 seconds

Not using TRY/CATCH in transactions, letting errors leave transactions open

Wrap all transactions in TRY/CATCH with ROLLBACK in CATCH block

When an error occurs in a transaction without TRY/CATCH, the transaction remains open. This holds locks on affected rows/tables, blocking other queries. In high-traffic systems, this causes cascading failures.

Example - Transfer money between accounts:

-- WRONG: No error handling (transaction left open on error)

BEGIN TRANSACTION;

UPDATE accounts SET balance = balance - 1000 WHERE account_id = 100;