The WHILE
loop in SQL Server is a control-flow statement that allows us to repeatedly execute a block of code as long as a specified condition is true. It’s useful for iterative tasks and processing data in batches.
Syntax
The basic syntax goes like this:
WHILE condition
BEGIN
-- Code to be executed
END
Key points:
- The condition is evaluated before each iteration.
- If the condition is true, the code block executes.
- If the condition is false, the loop terminates.
The SQL Server documentation provides the following, more specific, syntax (which is basically an elaboration on the above simple syntax):
WHILE boolean_expression
{ sql_statement | statement_block | BREAK | CONTINUE }
The following examples demonstrate the how the syntax translates to actual T-SQL code.
Example
Here’s an example of a simple WHILE
loop:
DECLARE @counter INT = 1;
WHILE @counter <= 5
BEGIN
PRINT 'Iteration: ' + CAST(@counter AS VARCHAR(2));
SET @counter = @counter + 1;
END
Result:
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5
WHILE
Loop with BREAK
The BREAK
argument causes an exit from the innermost WHILE
loop. Any statements that appear after the END
keyword, marking the end of the loop, are executed.
Example:
DECLARE @num INT = 1;
WHILE 1 = 1 -- Infinite loop
BEGIN
IF @num > 5
BREAK;
PRINT @num;
SET @num = @num + 1;
END
Result:
1
2
3
4
5
While this example uses an infinite loop, we usually want to avoid infinite loops. But the example demonstrates that we can break out of the loop based on a certain condition being met.
WHILE
Loop with CONTINUE
The CONTINUE
argument restarts a WHILE
loop. Any statements after the CONTINUE
keyword are ignored. CONTINUE
is often opened by an IF
test.
Example:
DECLARE @i INT = 0;
WHILE @i < 10
BEGIN
SET @i = @i + 1;
IF @i % 2 = 0
CONTINUE;
PRINT 'Odd number: ' + CAST(@i AS VARCHAR(2));
END
Result:
Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9
Nested WHILE
Loop
It’s possible to nest WHILE
loops, so that we have a loop within another loop.
Here’s a simple example:
DECLARE @outer_counter INT = 1;
DECLARE @inner_counter INT;
WHILE @outer_counter <= 3
BEGIN
PRINT 'Outer loop iteration: ' + CAST(@outer_counter AS VARCHAR(2));
SET @inner_counter = 1;
WHILE @inner_counter <= 5
BEGIN
PRINT ' Inner loop iteration: ' + CAST(@inner_counter AS VARCHAR(2));
SET @inner_counter = @inner_counter + 1;
END
PRINT 'Outer loop continues after inner loop completes';
SET @outer_counter = @outer_counter + 1;
END
PRINT 'All loops completed';
Output:
Outer loop iteration: 1
Inner loop iteration: 1
Inner loop iteration: 2
Inner loop iteration: 3
Inner loop iteration: 4
Inner loop iteration: 5
Outer loop continues after inner loop completes
Outer loop iteration: 2
Inner loop iteration: 1
Inner loop iteration: 2
Inner loop iteration: 3
Inner loop iteration: 4
Inner loop iteration: 5
Outer loop continues after inner loop completes
Outer loop iteration: 3
Inner loop iteration: 1
Inner loop iteration: 2
Inner loop iteration: 3
Inner loop iteration: 4
Inner loop iteration: 5
Outer loop continues after inner loop completes
All loops completed
If the inner loop contains a BREAK
, it exits to the next outermost loop. All the statements after the end of the inner loop run first, and then the next outermost loop restarts:
DECLARE @outer_counter INT = 1;
DECLARE @inner_counter INT;
WHILE @outer_counter <= 3
BEGIN
PRINT 'Outer loop iteration: ' + CAST(@outer_counter AS VARCHAR(2));
SET @inner_counter = 1;
WHILE @inner_counter <= 5
BEGIN
PRINT ' Inner loop iteration: ' + CAST(@inner_counter AS VARCHAR(2));
IF @inner_counter = 2
BEGIN
PRINT ' Breaking inner loop';
BREAK;
END
SET @inner_counter = @inner_counter + 1;
END
PRINT 'Outer loop continues after inner loop break';
SET @outer_counter = @outer_counter + 1;
END
PRINT 'All loops completed';
Output:
Outer loop iteration: 1
Inner loop iteration: 1
Inner loop iteration: 2
Breaking inner loop
Outer loop continues after inner loop break
Outer loop iteration: 2
Inner loop iteration: 1
Inner loop iteration: 2
Breaking inner loop
Outer loop continues after inner loop break
Outer loop iteration: 3
Inner loop iteration: 1
Inner loop iteration: 2
Breaking inner loop
Outer loop continues after inner loop break
All loops completed
WHILE
Loop for Data Processing
Suppose we create the following table:
-- Create the Orders table
CREATE TABLE Orders (
OrderID INT IDENTITY(1,1) PRIMARY KEY,
CustomerName VARCHAR(100),
OrderDate DATE,
TotalAmount DECIMAL(10,2),
Processed BIT DEFAULT 0
);
-- Insert sample data
INSERT INTO Orders (CustomerName, OrderDate, TotalAmount)
VALUES
('John Doe', '2024-07-20', 150.00),
('Jane Smith', '2024-07-21', 200.50),
('Bob Johnson', '2024-07-22', 75.25),
('Alice Brown', '2024-07-23', 300.00),
('Charlie Davis', '2024-07-24', 125.75);
-- View the initial data
SELECT * FROM Orders;
Result:
OrderID CustomerName OrderDate TotalAmount Processed
------- ------------- ------------------------ ----------- ---------
1 John Doe 2024-07-20T00:00:00.000Z 150 false
2 Jane Smith 2024-07-21T00:00:00.000Z 200.5 false
3 Bob Johnson 2024-07-22T00:00:00.000Z 75.25 false
4 Alice Brown 2024-07-23T00:00:00.000Z 300 false
5 Charlie Davis 2024-07-24T00:00:00.000Z 125.75 false
We can use the following code to “process” the orders, one by one with a WHILE
loop:
-- Process orders using a WHILE loop
DECLARE @id INT;
WHILE EXISTS (SELECT 1 FROM Orders WHERE Processed = 0)
BEGIN
SELECT TOP 1 @id = OrderID FROM Orders WHERE Processed = 0;
-- Process the order (in this example, we're just marking it as processed)
UPDATE Orders
SET Processed = 1
WHERE OrderID = @id;
-- Simulate some processing time
WAITFOR DELAY '00:00:01';
PRINT 'Processed OrderID: ' + CAST(@id AS VARCHAR(10));
END
Output:
(1 row affected)
Processed OrderID: 1
(1 row affected)
Processed OrderID: 2
(1 row affected)
Processed OrderID: 3
(1 row affected)
Processed OrderID: 4
(1 row affected)
Processed OrderID: 5
In the real world we would do some actual processing, but seeing as this is just a quick example, all we do is set the Processed
column to 1
(for true
) and simulate a bit of processing time.
Once that code has run, we can check the data in the table again:
SELECT * FROM Orders;
Result:
OrderID CustomerName OrderDate TotalAmount Processed
------- ------------- ------------------------ ----------- ---------
1 John Doe 2024-07-20T00:00:00.000Z 150 true
2 Jane Smith 2024-07-21T00:00:00.000Z 200.5 true
3 Bob Johnson 2024-07-22T00:00:00.000Z 75.25 true
4 Alice Brown 2024-07-23T00:00:00.000Z 300 true
5 Charlie Davis 2024-07-24T00:00:00.000Z 125.75 true
We can see that all orders have now been processed.
These examples demonstrate that the WHILE
loop can be a great tool in SQL Server for handling repetitive tasks. However, it’s a good idea to use it judiciously, as set-based operations are often more efficient for large-scale data processing.