After you can DECLARE and SET, you need to choose paths and repeat work. SQL PL in DB2 for z/OS gives you IF / ELSEIF / ELSE, a CASE statement, four loop styles (WHILE, REPEAT, LOOP, FOR), and the jump twins LEAVE and ITERATE. This page explains what each construct actually does, when the condition is tested, and how labels keep nested loops honest.
The IF statement runs different SQL procedure statements depending on search conditions. IBM’s shape is:
123456IF rating = 1 THEN SET new_salary = new_salary + (new_salary * 0.10); ELSEIF rating = 2 THEN SET new_salary = new_salary + (new_salary * 0.05); ELSE SET new_salary = new_salary + (new_salary * 0.02); END IF;
A search condition that is unknown (null compared with a value, for example) is not true. Processing continues to the next ELSEIF or ELSE — the same three-valued logic you already use in WHERE. If rating is NULL, none of the equality tests fire and ELSE runs. Guard with rating IS NULL if null means something else.
THEN/ELSEIF/ELSE each take an SQL procedure statement. That can be a single SET or UPDATE, or a nested IF, WHILE, or BEGIN … END if you need a block. A label may sit in front of IF for GOTO/LEAVE in unusual designs; everyday code rarely labels IF.
COMPARE: ELSEIF is not a second independent IF. After a true branch runs, later ELSEIF clauses are not evaluated. Two separate IF statements would both run if both conditions were true.
The CASE statement is a multi-way branch. It is not the CASE expression you write in a select list. Two forms:
Compare one expression to a list of values:
12345678910111213141516CASE rating WHEN 1 THEN UPDATE DSN8C10.EMP SET SALARY = SALARY * 1.10, BONUS = 1000 WHERE EMPNO = EMPNUMBR; WHEN 2 THEN UPDATE DSN8C10.EMP SET SALARY = SALARY * 1.05, BONUS = 500 WHERE EMPNO = EMPNUMBR; ELSE UPDATE DSN8C10.EMP SET SALARY = SALARY * 1.02 WHERE EMPNO = EMPNUMBR; END CASE;
Each WHEN has its own search condition (like ELSEIF):
12345678CASE WHEN V_BONUS > 0 AND V_SAL > 100000 THEN SET P_CLASS = 'HIGH'; WHEN V_BONUS > 0 THEN SET P_CLASS = 'BONUS'; ELSE SET P_CLASS = 'BASE'; END CASE;
First true WHEN wins. If none is true and there is no ELSE, Db2 raises a CASE-not-found condition (an exception you should either handle or avoid by coding ELSE). Always include ELSE for status flags so a new rating code does not blow up the CALL.
End the statement with END CASE. A CASE expression ends with END only: SET V = CASE WHEN … END. Mixing the two is a syntax error that looks “almost right” in a code review.
| Statement | When the condition is tested | Minimum executions |
|---|---|---|
| WHILE | Before the body | Zero times if the condition starts false/unknown |
| REPEAT | After the body (UNTIL true means leave) | Once |
| LOOP | None — you must LEAVE (or RETURN) | Until you exit |
| FOR | Once per row of the SELECT | Zero rows = body never runs |
WHILE tests at the top. Use it when zero trips are legal — for example a cursor already at end-of-data, or an array whose CARDINALITY is 0.
123456789SET DATEINDEX = 1; SET DATESCOUNT = CARDINALITY(MYDATES); WHILE DATEINDEX <= DATESCOUNT DO IF DAYOFWEEK(MYDATES[DATEINDEX]) IN (1, 7) THEN SET WEEKENDS[WEEKENDINDEX] = MYDATES[DATEINDEX]; SET WEEKENDINDEX = WEEKENDINDEX + 1; END IF; SET DATEINDEX = DATEINDEX + 1; END WHILE;
The search condition is re-evaluated before every iteration. If it is unknown, it is not true, so the loop ends. Increment your index inside the body or you will loop forever when the condition stays true.
Classic cursor pattern: FETCH once, WHILE flag = 0 DO process, FETCH again, END WHILE, with a CONTINUE handler for NOT FOUND that sets the flag. The extra FETCH before the loop is what makes WHILE safe when the result is empty.
REPEAT always runs the body once, then leaves when UNTIL is true. That matches “try something, then see if we are done” — including FETCH-until-not-found if you test SQLSTATE after FETCH inside the body.
123456SET V_TRIES = 0; REPEAT SET V_TRIES = V_TRIES + 1; -- attempt work UNTIL V_TRIES >= 3 OR V_OK = 1 END REPEAT;
UNTIL true means stop. That is the opposite mnemonic of WHILE true means continue. REPEAT UNTIL V_DONE = 1 is easy to invert by accident if you grew up on WHILE.
LOOP has no condition. Something in the body must exit: LEAVE, RETURN, or a handler that EXITs the compound. LOOP is the natural shape when the exit test sits in the middle (FETCH, then if not found LEAVE, else process the row).
12345678910OPEN C1; FETCH_LOOP: LOOP FETCH C1 INTO V_EMPNO, V_SAL; IF V_AT_END = 1 THEN LEAVE FETCH_LOOP; END IF; SET V_TOTAL = V_TOTAL + V_SAL; END LOOP FETCH_LOOP; CLOSE C1;
The label FETCH_LOOP appears before LOOP and optionally again after END LOOP. If you code the ending label, it must match. LEAVE FETCH_LOOP continues at CLOSE C1, not at END of the whole procedure — unless the loop is the last statement.
FOR walks every row of a select-statement. Db2 declares, opens, fetches, and closes an implicit cursor. You write the SELECT once and the body once per row.
12345678FOR EMPROW AS SELECT EMPNO, LASTNAME, SALARY FROM DSN8C10.EMP WHERE WORKDEPT = P_DEPT DO SET V_FULL = EMPROW.LASTNAME; SET V_TOTAL = V_TOTAL + EMPROW.SALARY; END FOR;
Both need a label that names a loop or compound statement currently executing.
1234567891011DEPT_LOOP: WHILE V_MORE = 1 DO FETCH C1 INTO V_DEPT, V_SAL; IF V_AT_END = 1 THEN LEAVE DEPT_LOOP; END IF; IF V_SAL IS NULL THEN ITERATE DEPT_LOOP; -- skip remaining body for this row END IF; SET V_TOTAL = V_TOTAL + V_SAL; END WHILE DEPT_LOOP;
Nested loops: LEAVE inner_label leaves only the inner loop. LEAVE outer_label leaves both. Without labels, you cannot say which loop you mean — so nested FETCH loops always get names.
GOTO label exists in SQL PL. It jumps to a labelled statement in the same scope with restrictions IBM documents (you cannot jump into a handler or into the middle of a FOR in arbitrary ways). Prefer structured LEAVE/ITERATE/IF so the next person can still see the exits.
Search conditions inside WHILE/REPEAT/IF still see SQLSTATE from the previous statement unless you SET flags in handlers. A CONTINUE handler for an error that occurred while evaluating the search condition of IF/CASE/WHILE/REPEAT resumes after END IF / END CASE / END WHILE / END REPEAT, not inside the THEN branch. That surprise is why many shops set V_AT_END in a handler and test only the flag in WHILE.
IF is a fork in the path: if the sign says “rating 1,” take the left trail; else if “rating 2,” take the middle; else take the default trail. CASE is a row of labelled doors — open the first door whose label matches. WHILE is “keep walking while the light is green” (you might never start). REPEAT is “take one step, then see if we are done.” LOOP is a roundabout with no exit sign — you must choose an exit (LEAVE). FOR is a teacher handing you each homework paper in a stack; when the stack is empty, class is over. ITERATE means “skip the rest of this paper, give me the next one.” LEAVE means “I am done with this whole stack.”
1. In SQL PL IF, when the search condition is unknown (null)?
2. WHILE versus REPEAT:
3. LEAVE label exits:
4. ITERATE label means:
5. A FOR statement in SQL PL: