PL/SQL & T-SQL (Oracle and SQL Server) Module 9 – PL/SQL foundations: blocks, types, SQL and exceptions
PL/SQL control structures and iterators
IF and ELSIF, CASE statements that can raise CASE_NOT_FOUND, loops with EXIT, CONTINUE and labels, then the iterators and extended CASE of newer releases.
What you will learn
- Use IF, ELSIF and ELSE, and predict which branch a NULL value takes
- Choose between a CASE statement and a CASE expression for a value that no WHEN matches
- Write basic, WHILE and FOR loops with EXIT, CONTINUE and labels
- Use the iteration controls of 21c and later to step, chain and filter a FOR loop and to walk a sparse collection
- Rewrite an extended CASE of 26ai as a searched CASE that also runs on 19c
Before you start
What you’ll learn · Before you start
What you will learn
- Use IF, ELSIF and ELSE, and predict which branch a NULL value takes
- Choose between a CASE statement and a CASE expression for a value that no WHEN matches
- Write basic, WHILE and FOR loops with EXIT, CONTINUE and labels
- Use the iteration controls of 21c and later to step, chain and filter a FOR loop and to walk a sparse collection
- Rewrite an extended CASE of 26ai as a searched CASE that also runs on 19c
Before you start
On this page
An order worth 900.00 costs nothing to deliver under the lesson’s rule, and so does an order whose value is still
NULL, because the IF statement that sets the fee sends every unknown value to its ELSE. PL/SQL control structures
decide which statements run and how often, and each has a rule like that one for the values nobody expected.
You will choose branches with IF and CASE, write the three kinds of loop, and then meet the iterators of newer
releases.
IF runs the first branch whose condition is TRUE
The practice orders are worth 360.00, 420.00 and 900.00, and a made-up rule charges 49.00 for delivery below 400.00,
29.00 below 800.00 and nothing from 800.00 up. IF … THEN … ELSIF … ELSE … END IF; states the rule in one statement.
Oracle tests the conditions from the top, runs the first branch whose condition is TRUE, and runs ELSE only when
none was. The keyword is ELSIF, one word without a second E.
A comparison with NULL is neither TRUE nor FALSE under three-valued logic: The logic SQL uses once NULL is involved: a comparison can be true, false or UNKNOWN, and IF, WHILE, WHERE and each WHEN of a CASE act only on true. More in “T-SQL control flow: IF, WHILE and CASE”, so an
order not priced yet fails both tests and lands in ELSE with a fee of 0.00. The second block of the script tests
IS NULL first and leaves that fee unset on purpose:
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- IF ... ELSIF ... ELSE sets the delivery fee of the three practice orders from their values (qty x unit_price):
-- 360.00, 420.00 and 900.00. An order not priced yet has the value NULL, fails every comparison and lands in ELSE,
-- so the second version tests IS NULL first. control_model.py prints the same fees. Nothing is stored.
SET SERVEROUTPUT ON
DECLARE
PROCEDURE show_fee (p_order PLS_INTEGER, p_value NUMBER) IS
v_fee NUMBER(6, 2);
BEGIN
IF p_value < 400 THEN
v_fee := 49.00;
ELSIF p_value < 800 THEN -- ELSIF: one word, no E before IF
v_fee := 29.00;
ELSE
v_fee := 0;
END IF;
DBMS_OUTPUT.PUT_LINE('order ' || p_order || ': value ' || NVL(TO_CHAR(p_value, 'FM9990.00'), 'NULL')
|| ', fee ' || TO_CHAR(v_fee, 'FM990.00'));
END show_fee;
BEGIN
show_fee(1, 360.00); -- 2 x TEA-250 at 180.00
show_fee(2, 420.00); -- 1 x RICE-5K at 420.00
show_fee(3, 900.00); -- 5 x TEA-250 at 180.00
show_fee(4, NULL); -- an order whose price is not known yet
END;
/
-- Expected output (Conditional Selection Statements: the first TRUE condition wins; no TRUE condition runs ELSE):
-- order 1: value 360.00, fee 49.00
-- order 2: value 420.00, fee 29.00
-- order 3: value 900.00, fee 0.00
-- order 4: value NULL, fee 0.00 <- NULL < 400 is not TRUE, and neither is NULL < 800
-- PL/SQL procedure successfully completed.
DECLARE
PROCEDURE show_fee (p_order PLS_INTEGER, p_value NUMBER) IS
v_fee NUMBER(6, 2);
BEGIN
IF p_value IS NULL THEN
v_fee := NULL; -- unknown on purpose: the caller must price the order first
ELSIF p_value < 400 THEN
v_fee := 49.00;
ELSIF p_value < 800 THEN
v_fee := 29.00;
ELSE
v_fee := 0;
END IF;
DBMS_OUTPUT.PUT_LINE('order ' || p_order || ': fee ' || NVL(TO_CHAR(v_fee, 'FM990.00'), 'not set'));
END show_fee;
BEGIN
show_fee(3, 900.00);
show_fee(4, NULL);
END;
/
-- Expected output:
-- order 3: fee 0.00
-- order 4: fee not set
-- PL/SQL procedure successfully completed. """if_branches.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""
from oracle_session import Lab
with Lab() as lab:
lab.sqlplus("branches/if_branches.sql") Output
$ sqlplus -s lab@//localhost/FREEPDB1 @branches/if_branches.sql order 1: value 360.00, fee 49.00 order 2: value 420.00, fee 29.00 order 3: value 900.00, fee 0.00 order 4: value NULL, fee 0.00 PL/SQL procedure successfully completed. order 3: fee 0.00 order 4: fee not set PL/SQL procedure successfully completed.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 if_branches_session.py
The model applies the same rules, and the CASE rules of the next section, to the same values. Press Edit, add an
order worth 799.99 and run it:
"""The choices and loops of the lesson's first three sections, worked out the way PL/SQL runs them.
1. IF ... ELSIF ... ELSE runs the first branch whose condition is TRUE. A
comparison with NULL is unknown, never TRUE, so an order whose value is NULL
skips every test and lands in ELSE (None stands for NULL here).
2. A CASE expression with no matching WHEN and no ELSE gives NULL; a CASE
statement raises CASE_NOT_FOUND (ORA-06592) instead.
3. The loops of loops.sql: FOR with CONTINUE WHEN and EXIT WHEN, and a WHILE
loop that rounds to the paisa each year, as ROUND(x, 2) does.
Edit ORDERS, KINDS or the balances and run it again.
"""
from decimal import ROUND_HALF_UP, Decimal
PAISA = Decimal("0.01")
ORDERS = [(1, Decimal("360.00")), (2, Decimal("420.00")), (3, Decimal("900.00")), (4, None)]
KINDS = ["savings", "current", "deposit"]
RATES = {"savings": Decimal("3"), "current": Decimal("0")}
BALANCES = {1: Decimal("2500.00"), 2: Decimal("18000.00"), 3: Decimal("760.25"), 4: Decimal("41250.50")}
def below(value, limit):
"""value < limit with NULL: None (unknown) when value is None."""
return None if value is None else value < limit
def fee(value):
if below(value, 400) is True:
return "IF", Decimal("49.00")
elif below(value, 800) is True:
return "ELSIF", Decimal("29.00")
else:
return "ELSE", Decimal("0.00")
print("1. Delivery fee by order value")
for order_id, value in ORDERS:
branch, amount = fee(value)
shown = "NULL" if value is None else value
print(f" order {order_id}: value {shown} -> {branch}, fee {amount}")
print()
print("2. Interest rate by account kind")
for kind in KINDS:
expression = RATES.get(kind)
print(f" {kind}: expression gives [{'' if expression is None else expression}]")
if kind in RATES:
print(f" {kind}: statement assigns {RATES[kind]}")
else:
print(f" {kind}: statement raises CASE_NOT_FOUND (ORA-06592)")
print()
print("3a. FOR id IN 1..4, CONTINUE WHEN balance < 1000, EXIT WHEN total > 50000")
total = Decimal("0")
for account_id in range(1, 5):
balance = BALANCES[account_id]
if balance < 1000:
print(f" account {account_id}: {balance} skipped by CONTINUE")
continue
total += balance
print(f" account {account_id}: {balance} added, total {total}")
if total > 50000:
print(f" EXIT after account {account_id}")
break
balance, years = Decimal("760.25"), 0
while balance < 1000:
balance = (balance * Decimal("1.03")).quantize(PAISA, rounding=ROUND_HALF_UP)
years += 1
print()
print(f"3b. WHILE balance < 1000 at 3 % a year: {years} years, ending at {balance}") Output
1. Delivery fee by order value order 1: value 360.00 -> IF, fee 49.00 order 2: value 420.00 -> ELSIF, fee 29.00 order 3: value 900.00 -> ELSE, fee 0.00 order 4: value NULL -> ELSE, fee 0.00 2. Interest rate by account kind savings: expression gives [3] savings: statement assigns 3 current: expression gives [0] current: statement assigns 0 deposit: expression gives [] deposit: statement raises CASE_NOT_FOUND (ORA-06592) 3a. FOR id IN 1..4, CONTINUE WHEN balance < 1000, EXIT WHEN total > 50000 account 1: 2500.00 added, total 2500.00 account 2: 18000.00 added, total 20500.00 account 3: 760.25 skipped by CONTINUE account 4: 41250.50 added, total 61750.50 EXIT after account 4 3b. WHILE balance < 1000 at 3 % a year: 10 years, ending at 1021.71
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 control_model.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
A CASE statement must match, or it raises an error
A simple CASE compares one value, the selector, with the value after each WHEN; a searched CASE has no selector
and tests a full condition after each WHEN. In both, the first match wins and nothing falls through to the next
WHEN. The difference that hurts lies elsewhere. A CASE expression without ELSE gives NULL when nothing matches,
while a CASE statement without ELSE raises the predefined exception CASE_NOT_FOUND, ORA-06592. Follow one
statement for the kinds current and deposit:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The selector is read once. show_rate('current') starts the CASE statement. Oracle evaluates the selector p_kind once, and then compares that one value with each WHEN in turn, from the top.
- The first WHEN does not match. 'current' is not 'savings', so the statement after the first WHEN is skipped and the comparison moves on to the next WHEN.
- The second WHEN matches. 'current' equals 'current': v_rate := 0 runs and the CASE statement ends. There is no fall-through, so no later WHEN is even looked at.
- A kind that no WHEN names. show_rate('deposit') runs the same statement. Neither WHEN matches, and the statement has no ELSE to run instead.
- The statement raises an error. With no match and no ELSE, a CASE statement raises the predefined exception CASE_NOT_FOUND, ORA-06592. The handler of show_rate catches it and prints a line instead of letting the error end the block.
- The expression gives NULL. The CASE expression earlier in show_rate met the same kind and returned NULL without any error, so v_rate printed as an empty pair of brackets.
The script sends three account kinds through both forms:
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- The same rule twice, interest rates by account kind: once as a CASE expression and once as a CASE statement,
-- neither with ELSE. For the kind 'deposit', which no WHEN names, the expression gives NULL and the statement
-- raises CASE_NOT_FOUND (ORA-06592). The rates are made up; control_model.py prints the same lines.
SET SERVEROUTPUT ON
DECLARE
PROCEDURE show_rate (p_kind VARCHAR2) IS
v_rate NUMBER(5, 2);
BEGIN
v_rate := CASE p_kind -- a CASE expression: it has a value
WHEN 'savings' THEN 3.00
WHEN 'current' THEN 0
END;
DBMS_OUTPUT.PUT_LINE(p_kind || ': expression gives [' || v_rate || ']');
CASE p_kind -- a CASE statement: it runs statements
WHEN 'savings' THEN v_rate := 3.00;
WHEN 'current' THEN v_rate := 0;
END CASE;
DBMS_OUTPUT.PUT_LINE(p_kind || ': statement assigns ' || v_rate);
EXCEPTION
WHEN CASE_NOT_FOUND THEN
DBMS_OUTPUT.PUT_LINE(p_kind || ': statement raised CASE_NOT_FOUND, SQLCODE ' || SQLCODE);
END show_rate;
BEGIN
show_rate('savings');
show_rate('current');
show_rate('deposit');
END;
/
-- Expected output (Expression page: a CASE expression without ELSE returns NULL; CASE Statement page: without ELSE,
-- no match raises CASE_NOT_FOUND):
-- savings: expression gives [3]
-- savings: statement assigns 3
-- current: expression gives [0]
-- current: statement assigns 0
-- deposit: expression gives []
-- deposit: statement raised CASE_NOT_FOUND, SQLCODE -6592
-- PL/SQL procedure successfully completed. """case_choice.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""
from oracle_session import Lab
with Lab() as lab:
lab.sqlplus("case/case_choice.sql") Output
$ sqlplus -s lab@//localhost/FREEPDB1 @case/case_choice.sql savings: expression gives [3] savings: statement assigns 3 current: expression gives [0] current: statement assigns 0 deposit: expression gives [] deposit: statement raised CASE_NOT_FOUND, SQLCODE -6592 PL/SQL procedure successfully completed.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 case_choice_session.py
A NULL selector never matches WHEN NULL, because NULL equals nothing, not even NULL. Oracle’s advice is a searched
CASE with WHEN kind IS NULL. Oracle also allows a handler for CASE_NOT_FOUND in the block’s exception section in
place of the ELSE.
Checkpoint
Check: IF, ELSIF and ELSE · CASE statements and CASE expressions
A loop repeats until EXIT, its test or its range ends it
PL/SQL control structures for repetition come in three kinds. A basic LOOP repeats until an EXIT, an EXIT WHEN
or an error ends it. WHILE tests its condition before every pass. FOR v_id IN 1 .. 4 declares its own index, a
PLS_INTEGER that the body may read but never assign, counts up, or down with REVERSE, and forgets the index after
END LOOP. In any loop, CONTINUE WHEN ends one pass early and EXIT WHEN ends the loop. T-SQL has only WHILE, as
T-SQL control flow shows. Here is one loop on the practice accounts:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- Pass 1 adds 2500.00. The loop declares v_id itself and sets it to 1. Account 1 holds 2500.00, which is not below 1000, so the pass goes on and the total becomes 2500.00.
- Pass 2 adds 18000.00. Account 2 holds 18000.00. The total, 20500.00, is not above 50000, so EXIT WHEN lets the loop continue with the next id.
- Pass 3 is cut short. Account 3 holds 760.25. CONTINUE WHEN is true, so the rest of this pass is skipped: nothing is added, nothing is printed, and control goes back to the FOR header.
- Pass 4 passes the limit. Account 4 adds 41250.50 and the total reaches 61750.50. Now EXIT WHEN is true and the loop ends here; with a longer range, any later ids would never be read.
- After the loop. Control continues after END LOOP. v_id no longer exists there: a FOR loop index belongs to the loop, and the loop body could read it but never assign it.
A loop label: A name written in double angle brackets just before a PL/SQL loop and repeated after its END LOOP; EXIT and CONTINUE can name it to act on that loop from inside a nested one. lets an inner loop leave both loops at once: EXIT rows WHEN r * c = 4
ends the outer loop named rows, while a plain EXIT leaves only the inner one. The script holds all three kinds:
-- Engine: Oracle AI Database 26ai Free, Release 23.26.3.0.0; user LAB in FREEPDB1. Client: SQL*Plus or SQLcl.
-- Three loops on the practice accounts: a FOR loop that skips small balances with CONTINUE WHEN and stops with
-- EXIT WHEN, a WHILE loop that grows 760.25 at 3 % a year until it reaches 1000.00, and two nested FOR loops that
-- a labelled EXIT leaves together. control_model.py prints the same numbers. Nothing is stored.
SET SERVEROUTPUT ON
DECLARE
v_balance accounts.balance%TYPE;
v_total NUMBER(14, 2) := 0;
BEGIN
FOR v_id IN 1 .. 4 LOOP -- v_id: declared by the loop, PLS_INTEGER, read-only
SELECT balance INTO v_balance FROM accounts WHERE account_id = v_id;
CONTINUE WHEN v_balance < 1000; -- skip the rest of this pass only
v_total := v_total + v_balance;
DBMS_OUTPUT.PUT_LINE('account ' || v_id || ' added, total ' || TO_CHAR(v_total, 'FM99999990.00'));
EXIT WHEN v_total > 50000; -- leave the loop for good
END LOOP;
END;
/
-- Expected output (accounts 1 to 4 hold 2500.00, 18000.00, 760.25 and 41250.50):
-- account 1 added, total 2500.00
-- account 2 added, total 20500.00
-- account 4 added, total 61750.50
-- PL/SQL procedure successfully completed.
-- Account 3 is skipped by CONTINUE WHEN, and EXIT WHEN ends the loop after account 4.
DECLARE
v_balance NUMBER(14, 2) := 760.25;
v_years PLS_INTEGER := 0;
BEGIN
WHILE v_balance < 1000 LOOP -- tested before every pass
v_balance := ROUND(v_balance * 1.03, 2);
v_years := v_years + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_years || ' years, ending at ' || TO_CHAR(v_balance, 'FM99990.00'));
END;
/
-- Expected output: 10 years, ending at 1021.71
BEGIN
<<rows>>
FOR r IN 1 .. 3 LOOP
FOR c IN 1 .. 3 LOOP
EXIT rows WHEN r * c = 4; -- leaves BOTH loops: the label names the outer one
DBMS_OUTPUT.PUT_LINE(r || ' x ' || c || ' = ' || r * c);
END LOOP;
END LOOP rows;
DBMS_OUTPUT.PUT_LINE('left both loops');
END;
/
-- Expected output:
-- 1 x 1 = 1
-- 1 x 2 = 2
-- 1 x 3 = 3
-- 2 x 1 = 2
-- left both loops
-- Writing r := 2; inside the loop would not compile (PLS-00363: the index cannot be an assignment target). """loops.sql in the lab: SQL*Plus runs it as LAB on a freshly loaded practice schema."""
from oracle_session import Lab
with Lab() as lab:
lab.sqlplus("loops/loops.sql") Output
$ sqlplus -s lab@//localhost/FREEPDB1 @loops/loops.sql account 1 added, total 2500.00 account 2 added, total 20500.00 account 4 added, total 61750.50 PL/SQL procedure successfully completed. 10 years, ending at 1021.71 PL/SQL procedure successfully completed. 1 x 1 = 1 1 x 2 = 2 1 x 3 = 3 2 x 1 = 2 left both loops PL/SQL procedure successfully completed.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 loops_session.py
Its WHILE loop needs 10 years at 3 % to grow 760.25 past 1000.00, ending at 1021.71.
A basic loop with no way out
Wrong: an EXIT WHEN that tests a variable the loop body never changes, so the loop runs until the session is
stopped. Right: change the tested value in every pass, or use FOR, whose range ends the loop by itself.
Newer releases add iterators and extended CASE
Oracle Database 21c gave the FOR loop new iterators, built from
iteration control: The part of a PL/SQL FOR loop header after IN that produces the index values: a range such as 1 .. 10, or, from Oracle Database 21c, a stepped range, a list of controls or a collection. that produce the index values, and 26ai extended the simple
CASE statement. Code that must also run on 19c can use neither:
- 19c
Classic loops and both kinds of CASE long-term release
Basic LOOP, WHILE and FOR i IN low..high, with EXIT, CONTINUE and labels; IF, the simple CASE that compares values and the searched CASE that tests conditions. Code that must run on 19c keeps to these.
- 21c
Iteration controls in the FOR loop new syntax
The FOR loop gains iterators: stepped ranges with BY, several controls in one header, WHEN and WHILE filters, a declared type for the index, and loops over the indexes and values of a collection.
- 26ai
Extended CASE controls new syntax
A simple CASE statement accepts dangling predicates such as < 0 or > 100 after WHEN, and comma lists of choices, so one WHEN can name several values.
The timeline has 3 events, from 19c to 26ai.
Each new form has a plain meaning. A stepped range such as 1 .. 10 BY 3 moves by more than one. Several controls
separated by commas run one after another in a single loop. A WHEN filter skips values whose condition is not
TRUE. A declared index type, such as NUMBER(3,1), allows fractional steps. INDICES OF, VALUES OF and PAIRS OF
walk the indexes, the values or both of a collection. A dangling predicate: A comparison with its left operand missing, such as < 0 or BETWEEN 4 AND 30, written after WHEN in a simple CASE statement of Oracle AI Database 26ai; the CASE selector fills the gap. is a
comparison with its left side missing, such as < 0, and a choice list puts several values after one WHEN.
Checkpoint
Check: Basic, WHILE and FOR loops · Which release adds which form
Stepped, chained and filtered FOR loops, and sparse collections walked safely Pro: opens with a Pro, Premium or Ultimate pass
The iteration controls of 21c change what a single FOR header can say. Each one runs below on the lesson’s values,
next to the classic loop it replaces.
In this part:
- Stepping, chaining and filtering one FOR header
- Walking a sparse collection without a missing-index error
3 runnable examples (Python, SQL) · about 7 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Extended CASE in 26ai, and the searched CASE that runs on 19c Pro: opens with a Pro, Premium or Ultimate pass
A late fee by days overdue shows the extended CASE controls at work. The same rules then move to a searched CASE,
line by line, so that the code also runs on 19c.
In this part:
- Dangling predicates and choice lists in one CASE statement
- Rewriting it for 19c, and which form to keep
- Testing a rule table at its boundaries, with a slip reproduced
3 runnable examples (Python, SQL) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Practise: predict which branch runs and when a loop stops
Each question gives a few lines of PL/SQL. Decide which statements run and what the variables hold before you choose:
Practice that adapts to you
Practise this lesson
9 questions about 8 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): how do EXIT and CONTINUE differ inside a loop?
EXIT ends the whole loop and control moves to the statement after END LOOP; CONTINUE ends only the current
pass and starts the next one. Both have a WHEN form that acts only when its condition is TRUE, and both can name a
label to act on an enclosing loop from inside a nested one.
Three longer questions follow, on a new status code that broke a nightly job, deletes that broke a loop, and a port to 19c:
-
Fresher to senior
A nightly settlement job maps each order's status to an action with a CASE statement on new, paid, shipped and cancelled. After the shop adds a returned status, the job stops with ORA-06592 on the first such order. Explain the failure and how you would change the code and its tests.
-
Mid to senior
A procedure loads pending orders into an associative array indexed by order id, deletes those that fail a check, then sends the rest with FOR i IN 1 .. v_orders.COUNT. It passed its tests and now fails with ORA-01403. What is wrong, and how would you write the loop on 19c and on 26ai?
-
Mid to staff
Your team wrote a billing package on Oracle AI Database 26ai with FOR loops that use BY and WHEN, and simple CASE statements with dangling predicates. A client must run it on Oracle Database 19c. How do you plan the port, and how do you show that the 19c version behaves the same?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
How does IF ... ELSIF ... ELSE choose a branch, and where does a NULL value land?
Oracle tests the conditions from the top, runs the first branch whose condition is TRUE and runs ELSE only when none was. Comparing a value with NULL gives neither TRUE nor FALSE, so an unpriced order fails every test and reaches ELSE. The keyword is ELSIF, one word without a second E.
-
What happens when nothing matches a CASE, and there is no ELSE?
A CASE expression gives NULL; a CASE statement raises the predefined exception CASE_NOT_FOUND, ORA-06592. A handler for CASE_NOT_FOUND in the block's exception section can stand in place of the ELSE.
-
How do a simple and a searched CASE differ, and why does a NULL selector need the searched form?
A simple CASE compares one selector with the value after each WHEN; a searched CASE has no selector and tests a full condition. In both, the first match wins and nothing falls through. A NULL selector never matches WHEN NULL, because NULL equals nothing, not even NULL, so use WHEN kind IS NULL.
-
What are PL/SQL's three kinds of loop?
A basic LOOP repeats until an EXIT, an EXIT WHEN or an error ends it. WHILE tests its condition before every pass. FOR declares its own index, a PLS_INTEGER the body may read but never assign, counts up or down with REVERSE, and forgets the index after END LOOP.
-
What do CONTINUE WHEN, EXIT WHEN and a loop label each do?
CONTINUE WHEN ends one pass early, EXIT WHEN ends the loop, and a label lets an inner loop leave an outer one: EXIT rows WHEN ... ends the loop named rows, where a plain EXIT would leave only the inner loop.
-
Which EXIT WHEN never fires?
One that tests a variable the loop body never changes: the loop then runs until the session is stopped. Change the tested value on every pass, or use FOR, whose range ends the loop by itself.
-
What did Oracle Database 21c add to the FOR loop?
Iteration controls that produce the index values: a stepped range such as 1 .. 10 BY 3, several controls separated by commas running one after another in one loop, a WHEN filter that skips values whose condition is not TRUE, a declared index type for fractional steps, and INDICES OF, VALUES OF and PAIRS OF for collections.
-
What did 26ai add to the simple CASE statement, and what may 19c code use?
A dangling predicate, a comparison with its left side missing such as < 0, and a choice list that puts several values after one WHEN. Code that must also run on 19c can use neither those nor the 21c iterators.
Key takeaways
IF … ELSIF … ELSEruns the first branch whose condition is TRUE, and a NULL value fails every test and reachesELSE.- A
CASEstatement that matches nothing and has noELSEraisesCASE_NOT_FOUND; aCASEexpression gives NULL. - Basic,
WHILEandFORloops end byEXIT, by their test or by their range;CONTINUEends one pass, and a label lets an inner loop leave an outer one. - PL/SQL control structures gained iterators in 21c and extended
CASEcontrols in 26ai, and 19c code uses neither.
Terms in this lesson
- dangling predicate
- A comparison with its left operand missing, such as < 0 or BETWEEN 4 AND 30, written after WHEN in a simple CASE statement of Oracle AI Database 26ai; the CASE selector fills the gap.
- iteration control
- The part of a PL/SQL FOR loop header after IN that produces the index values: a range such as 1 .. 10, or, from Oracle Database 21c, a stepped range, a list of controls or a collection.
- loop label
- A name written in double angle brackets just before a PL/SQL loop and repeated after its END LOOP; EXIT and CONTINUE can name it to act on that loop from inside a nested one.
- sparse collection
- A PL/SQL collection whose indexes have gaps, such as an associative array that holds elements at indexes 1, 10 and 100 only.
References
- PL/SQL Language Reference 26ai: Conditional Selection Statements (Oracle)
- PL/SQL Language Reference 26ai: CASE Statement (Oracle)
- PL/SQL Language Reference 26ai: Expression (Oracle)
- PL/SQL Language Reference 26ai: Predefined Exceptions (Oracle)
- Database Error Messages: ORA-06592 (Oracle)
- PL/SQL Language Reference 26ai: LOOP Statements (Oracle)
- PL/SQL Language Reference 26ai: FOR LOOP Statement (Oracle)
- PL/SQL Language Reference 26ai: Basic LOOP Statement (Oracle)
- PL/SQL Language Reference 26ai: EXIT Statement (Oracle)
- PL/SQL Language Reference 26ai: CONTINUE Statement (Oracle)
- PL/SQL Language Reference 26ai: WHILE LOOP Statement (Oracle)
- PL/SQL Language Reference 26ai: Iterator (Oracle)
- PL/SQL Language Reference 26ai: Collection Methods (Oracle)
- PL/SQL Language Reference 26ai: Associative Arrays (Oracle)
- PL/SQL Language Reference 21c: Changes in This Release (Oracle)
- PL/SQL Language Reference 26ai: Changes in This Release (Oracle)
Report a problem with this lesson
Kept only in this browser. Your Learn progress