Loops in Oracle
Let’s delve into the world of PL/SQL loops, which are essential for controlling the flow of commands within your Oracle database programs. PL/SQL, Oracle Corporation’s procedural extension for SQL, allows you to codify business rules, trigger database events, and add programming logic to SQL commands. PL/SQL code is structured into blocks, which can be anonymous or named (like stored procedures or packages). Each block typically has a Declarations section, an Executable Commands section, and an optional Exception Handling section. Loops are a core part of the Executable Commands section, enabling repetitive execution of code until a certain condition is met.
For displaying output from PL/SQL blocks, remember to execute SET SERVEROUTPUT ON;
in your SQL*Plus or SQL Developer session.
Understanding the PL/SQL Block Structure
Since all loop types are found in the Executable Commands section, it’s useful to review the fundamental structure of a PL/SQL block before delving into loops:
DECLARE
-- Optional: Declarations section to define variables, cursors, etc.
-- Example: pi constant NUMBER(9,7) := 3.1415927;
-- Example: radius INTEGER(5);
BEGIN
-- Mandatory: Executable Commands section where program logic resides
-- This is where loops and conditional statements are used
EXCEPTION
-- Optional: Exception Handling section to manage errors
END;
/ -- The slash (/) executes the PL/SQL block
Simple Loops
The simplest type of loop in PL/SQL is called a simple loop. The fundamental characteristic of a simple loop is that it repeats until an EXIT
or EXIT WHEN
statement is met within the loop’s body. It is distinguished by its simple form, which starts with the LOOP
keyword and ends with END LOOP
. A basic loop is guaranteed to run its body (or at least a portion of it) at least once because the termination condition is tested inside the loop. This makes it helpful when you want to make sure initial processing happens but aren’t sure how many iterations are required.
The EXIT WHEN
statement, a more popular variation, incorporates an IF-THEN
condition straight into the exit logic, making the code more compact for single termination conditions. The EXIT
statement unconditionally ends the loop, passing control to the statement that comes right after END LOOP
.
Syntax:
LOOP
-- Block of code to be repeated
-- ...
EXIT WHEN condition; -- Condition to exit the loop
-- ...
END LOOP;
Creating Table and Inserting Values: Let’s create a table named AREAS
to store the calculated areas of circles.
-- Create the AREAS table if it doesn't exist
CREATE TABLE AREAS (
RADIUS NUMBER(5),
AREA NUMBER(14,2)
);
Code Example: This example demonstrates a simple loop that calculates the area of circles, starting with a radius of 3 and incrementing by 1 in each iteration. The loop continues to insert records into the AREAS
table until the calculated AREA
exceeds 100.
SET SERVEROUTPUT ON;
DELETE FROM AREAS;
COMMIT; -- Clear previous data for a fresh run
DECLARE
pi CONSTANT NUMBER(9,7) := 3.1415927; -- Declare constant for PI
radius INTEGER(5); -- Declare variable for radius
area NUMBER(14,2); -- Declare variable for area
BEGIN
radius := 3; -- Initialize radius
LOOP
area := pi * POWER(radius, 2); -- Calculate area
INSERT INTO AREAS VALUES (radius, area); -- Insert values into AREAS table
radius := radius + 1; -- Increment radius
EXIT WHEN area > 100; -- Exit condition: loop terminates when area exceeds 100
END LOOP;
DBMS_OUTPUT.PUT_LINE('Simple loop completed. Check the AREAS table for results.');
END;
/
Output:
PL/SQL procedure successfully completed.
-- Verify the inserted data
SELECT * FROM AREAS ORDER BY RADIUS;
RADIUS AREA
---------- ----------
3 28.27
4 50.27
5 78.54
6 113.10
The loop processed radius values 3, 4, 5, and 6, as you can see. The EXIT WHEN
condition was satisfied when the radius was 6 because the area (113.10) was more than 100, and no additional data were processed after that iteration.
WHILE Loops
The definition of a WHILE
loop is a conditional loop that keeps running as long as the Boolean condition that is stated at the loop border evaluates to TRUE
. The fact that the condition is tested at the start of each iteration is a crucial feature of the WHILE loop. This implies that the body of the loop won’t run at all if the condition is set to FALSE
or NULL
at the beginning. Because of this, the WHILE loop is appropriate in situations where you are unsure of the precise number of iterations and the loop may not even need to run.
The termination criteria are specified in the WHILE
statement itself. The loop is stopped when the radius
value violates the WHILE
condition.
Syntax:
WHILE condition LOOP -- Condition evaluated at the start of each iteration
-- Block of code to be repeated
-- ...
END LOOP;
Reusing Table: We will reuse the AREAS
table for this example.
Code Example: This WHILE loop also calculates and inserts areas into the AREAS
table, but it explicitly checks if radius
is less than or equal to 7 before each iteration. The loop terminates once radius
becomes greater than 7.
SET SERVEROUTPUT ON;
DELETE FROM AREAS;
COMMIT; -- Clear previous data
DECLARE
pi CONSTANT NUMBER(9,7) := 3.1415927; -- Declare constant for PI
radius INTEGER(5); -- Declare variable for radius
area NUMBER(14,2); -- Declare variable for area
BEGIN
radius := 3; -- Initialize radius
WHILE radius <= 7 LOOP -- Loop continues as long as radius is 7 or less
area := pi * POWER(radius, 2); -- Calculate area
INSERT INTO AREAS VALUES (radius, area); -- Insert values into AREAS table
radius := radius + 1; -- Increment radius
END LOOP;
DBMS_OUTPUT.PUT_LINE('WHILE loop completed. Check the AREAS table for results.');
END;
/
Output:
PL/SQL procedure successfully completed.
-- Verify the inserted data
SELECT * FROM AREAS ORDER BY RADIUS;
RADIUS AREA
---------- ----------
3 28.27
4 50.27
5 78.54
6 113.10
7 153.94
For radius
values ranging from 3 to 7, inclusive, the loop ran. The loop ended when radius
reached 8, making the condition radius <= 7
false.
Numeric FOR Loops
To put it simply, a Numeric FOR loop iterates a predetermined number of times, making it a “counted” loop. A start..end
range indicates the number of iterations, which is known at the beginning of the loop. Because PL/SQL implicitly declares the loop counter (or index) as an INTEGER
, its scope is restricted to the loop. This means that PL/SQL takes care of this automatically, so you don’t have to declare the counter variable in the DECLARE
section or manually increment it. When the loop starts, the range expressions (start
and end
) are only evaluated once.
The REVERSE
keyword, which counts down from the end
value to the start
value, can also be used to iterate in reverse order.
Syntax:
FOR counter IN [REVERSE] start_value .. end_value LOOP -- `counter` implicitly declared
-- Block of code to be repeated
-- ...
END LOOP;
Reusing Table: We will reuse the AREAS
table for this example.
Code Example: This Numeric FOR loop calculates and inserts areas for radius
values ranging from 1 through 7, inclusive.
SET SERVEROUTPUT ON;
DELETE FROM AREAS;
COMMIT; -- Clear previous data
DECLARE
pi CONSTANT NUMBER(9,7) := 3.1415927; -- Declare constant for PI
-- radius INTEGER(5); -- Not explicitly declared, handled by FOR loop
area NUMBER(14,2); -- Declare variable for area
BEGIN
-- The 'radius' variable is implicitly declared and iterates from 1 to 7
FOR radius IN 1..7 LOOP
area := pi * POWER(radius, 2); -- Calculate area
INSERT INTO AREAS VALUES (radius, area); -- Insert values into AREAS table
END LOOP;
DBMS_OUTPUT.PUT_LINE('Numeric FOR loop completed. Check the AREAS table for results.');
-- Example with REVERSE keyword
DBMS_OUTPUT.PUT_LINE('Numeric FOR loop with REVERSE:');
FOR counter IN REVERSE 1..5 LOOP
DBMS_OUTPUT.PUT_LINE('Current iteration of loop is ' || counter);
END LOOP;
END;
/
Output:
PL/SQL procedure successfully completed.
-- Verify the inserted data from the first loop
SELECT * FROM AREAS ORDER BY RADIUS;
RADIUS AREA
---------- ----------
1 3.14
2 12.57
3 28.27
4 50.27
5 78.54
6 113.10
7 153.94
7 rows selected.
Numeric FOR loop with REVERSE:
Current iteration of loop is 5
Current iteration of loop is 4
Current iteration of loop is 3
Current iteration of loop is 2
Current iteration of loop is 1
PL/SQL procedure successfully completed.
As the radius
counter iterated from 1 to 7, the first loop was able to correctly insert 7 records. By printing the counter from 5 down to 1, the second loop illustrated the REVERSE
keyword.
Cursor FOR Loops
Within a PL/SQL block, a Cursor FOR loop offers a very practical means of iterating over the output of a SQL query. Its main benefit is that it greatly simplifies the code by handling the cursor’s OPEN
, FETCH
, and CLOSE
instructions implicitly. This lowers the amount of code needed and removes a frequent cause of mistakes, such as failing to close a cursor.
PL/SQL automatically creates a record variable (with the %ROWTYPE
attribute) that corresponds to the structure of the query’s result set when a Cursor FOR loop is used. The column values of every fetched row are then accessed using this record variable. When every record from the related cursor has been retrieved, the loop ends unconditionally. The body of the loop won’t run if the query yields no rows.
Syntax:
FOR record_name IN (SELECT_statement) LOOP -- `record_name` is implicitly declared
-- Block of code to process each row
-- Access columns using record_name.column_name
END LOOP;
Alternatively, you can define a named cursor in the DECLARE
section and reference it:
CURSOR cursor_name IS SELECT_statement;
-- ...
FOR record_name IN cursor_name LOOP
-- ...
END LOOP;
Creating Table and Inserting Values: First, let’s ensure our RADIUS_VALS
table is set up with some data, and clear the AREAS
table.
-- Create the RADIUS_VALS table
CREATE TABLE RADIUS_VALS (
RADIUS NUMBER(5)
);
-- Insert values into RADIUS_VALS
INSERT INTO RADIUS_VALS VALUES (3);
INSERT INTO RADIUS_VALS VALUES (4);
INSERT INTO RADIUS_VALS VALUES (10);
COMMIT; -- Commit the inserts
-- Verify RADIUS_VALS content
SELECT * FROM RADIUS_VALS ORDER BY RADIUS;
Output (RADIUS_VALS setup):
Table RADIUS_VALS created.
1 row created.
1 row created.
1 row created.
Commit complete.
RADIUS
----------
3
4
10
Code Example: This Cursor FOR loop queries the RADIUS_VALS
table and, for each record found, calculates the area and inserts it into the AREAS
table.
SET SERVEROUTPUT ON;
DELETE FROM AREAS;
COMMIT; -- Clear previous data
DECLARE
pi CONSTANT NUMBER(9,7) := 3.1415927; -- Declare constant for PI
area NUMBER(14,2); -- Declare variable for area
-- Define a named cursor for RADIUS_VALS
CURSOR rad_cursor IS
SELECT radius FROM RADIUS_VALS;
BEGIN
-- The 'rad_val' record is implicitly declared and populated by the cursor FOR loop
FOR rad_val IN rad_cursor LOOP
area := pi * POWER(rad_val.radius, 2); -- Calculate area using fetched radius
INSERT INTO AREAS VALUES (rad_val.radius, area); -- Insert into AREAS table
END LOOP;
DBMS_OUTPUT.PUT_LINE('Cursor FOR loop completed. Check the AREAS table for results.');
END;
/
Output:
PL/SQL procedure successfully completed.
-- Verify the inserted data
SELECT * FROM AREAS ORDER BY RADIUS;
RADIUS AREA
---------- ----------
3 28.27
4 50.27
10 314.16
Without the need for explicit OPEN
, FETCH
, or CLOSE
instructions, the loop processed each entry from RADIUS_VALS
, calculating and adding the associated areas into the AREAS
table.
CONTINUE Statement
The CONTINUE
statement, which was added in Oracle Database 11g, allows you to bypass any leftover statements in the current loop iteration and go straight to the next one. This can make intricate conditional logic inside a loop body simpler.
Two types exist:
CONTINUE;
: Jumps straight to the following iteration.CONTINUE WHEN condition;
: If the given condition isTRUE
, it moves on to the following iteration.
For more precise control over loop execution, you can also use CONTINUE
to give a loop label that will tell which loop (in nested loops) should move on to its next iteration.
Syntax:
LOOP_LABEL: -- Optional loop label
FOR counter IN start..end LOOP
-- ...
CONTINUE [LOOP_LABEL] [WHEN condition]; -- Jump to next iteration
-- ...
END LOOP;
Code Example: This example uses a Numeric FOR loop and the CONTINUE WHEN
statement to skip even numbers. Only odd numbers will be displayed.
SET SERVEROUTPUT ON;
BEGIN
DBMS_OUTPUT.PUT_LINE('Demonstrating CONTINUE WHEN:');
FOR l_index IN 1 .. 10 LOOP
CONTINUE WHEN MOD(l_index, 2) = 0; -- If l_index is even, skip to next iteration [65]
DBMS_OUTPUT.PUT_LINE('Loop index = ' || TO_CHAR(l_index));
END LOOP;
DBMS_OUTPUT.PUT_LINE('CONTINUE WHEN example completed.');
DBMS_OUTPUT.PUT_LINE(CHR(10) || 'Demonstrating CONTINUE with nested loops:'); -- CHR(10) for newline
<<outer_loop>>
FOR outer_idx IN 1 .. 3 LOOP
DBMS_OUTPUT.PUT_LINE('Outer index = ' || TO_CHAR(outer_idx));
<<inner_loop>>
FOR inner_idx IN 1 .. 3 LOOP
DBMS_OUTPUT.PUT_LINE(' Inner index = ' || TO_CHAR(inner_idx));
CONTINUE outer_loop WHEN inner_idx = 1; -- Skip to next outer_loop iteration if inner_idx is 1
DBMS_OUTPUT.PUT_LINE(' This line will only show if inner_idx > 1 and after outer_loop continue.');
END LOOP inner_loop;
END LOOP outer_loop;
DBMS_OUTPUT.PUT_LINE('CONTINUE with nested loops example completed.');
END;
/
Output:
PL/SQL procedure successfully completed.
Demonstrating CONTINUE WHEN:
Loop index = 1
Loop index = 3
Loop index = 5
Loop index = 7
Loop index = 9
CONTINUE WHEN example completed.
Demonstrating CONTINUE with nested loops:
Outer index = 1
Inner index = 1
Outer index = 2
Inner index = 1
Outer index = 3
Inner index = 1
CONTINUE with nested loops example completed.
PL/SQL procedure successfully completed.
In the first part of the example, CONTINUE WHEN MOD(l_index, 2) = 0
caused the DBMS_OUTPUT.PUT_LINE
for even numbers to be skipped, displaying only odd numbers. In the second part, CONTINUE outer_loop WHEN inner_idx = 1
immediately caused the outer_loop
to proceed to its next iteration as soon as inner_idx
was 1, effectively preventing the rest of the inner loop’s body from executing and resetting the inner loop for the next outer iteration.
This includes thorough descriptions and illustrations of PL/SQL’s Simple Loops, WHILE Loops, Numeric FOR Loops, Cursor FOR Loops, and CONTINUE statement.