Als Stepped Control wird die altbekannte Angabe von Untergrenze und Obergrenze zur Steuerung der Schleifendurchläufe bezeichntet.
Schrittweite
Die erste – überfällige - Erweiterung betrifft die Schrittweite. Bisher konnte nur um 1 hochgezählt bzw. heruntergezählt werden. Brauchte man andere Iterationen, so behalf man sich bisher gerne mit Modulo-Division:
BEGIN
FOR i IN 2..10 LOOP
IF MOD (i, 2) = 0
THEN
DBMS_OUTPUT.put_line (i);
END IF;
END LOOP;
END;
/
Mit Version 21c geht das deutlich schlanker. Dafür wurde die neue BY-Klausel eingeführt:
BEGIN
FOR i IN 2 .. 10 BY 2 LOOP
DBMS_OUTPUT.put_line (i);
END LOOP;
END;
/
Für beide Schleifen ist die Ausgabe identisch:
2
4
6
8
10
Schleifenzähler und Schrittweite müssen noch nicht einmal mehr ganzzahlig sein. Wenn beim Schleifenzähler der Datentyp mit angegeben wird, sind auch Nachkommastellen möglich:
BEGIN
FOR i NUMBER (3,1) IN 2.7..10 BY 2 LOOP
DBMS_OUTPUT.put_line (i);
END LOOP;
END;
/
2,7
4,7
6,7
8,7
BEGIN
FOR i NUMBER IN 2.7..4 BY 0.5 LOOP
DBMS_OUTPUT.put_line (i);
END LOOP;
END;
/
2,7
3,2
3,7
Nur bei expliziter Angabe von NUMBER werden Bruchzahlen ausgegeben, und auch nur dann ist eine nicht-ganzzahlige Schrittweite möglich.
Ohne diese Angabe bleibt das Verhalten sinnvollerweise das gleiche wie bisher auch: Es wird gerundet. Das verhindert, dass ein Upgrade plötzlich unerwartete Seiteneffekte hat.
Kombinationen
Jetzt können, durch Kommata getrennt, auch mehrere Iterationsangaben beliebig kombiniert werden:
BEGIN
FOR i IN 1..3, 9..11, REVERSE 14..17 LOOP
DBMS_OUTPUT.put_line (i);
END LOOP;
END;
/
1
2
3
9
10
11
17
16
15
14
Nur der Vollständigkeit halber sei erwähnt, dass es durch Angabe des Schlüsselworts MUTABLE auch möglich wird, innerhalb der Schleife schreibend auf den Schleifenzähler zuzugreifen, was bisher ein absolutes Tabu war; davon wird aber dringend abgeraten!
Zusätzlich können beliebige Bedingungen angegeben werden, um Schleifendurchläufe weiter einzugrenzen, entweder mit WHILE (Stopping Predicate) oder mit WHEN (Skipping Predicate):
BEGIN
FOR i IN 1..10 WHEN MOD(i,3) = 0, i+1..15 WHILE i < 13 LOOP
DBMS_OUTPUT.put_line (i);
END LOOP;
END;
/
3
6
9
11
12
Wie man an diesem Beispiel sieht, kann man bei der Angabe weiterer Iterationen auch Bezug nehmen auf den Wert des Schleifenzählers nach Beendigung der vorhergegangenen Iterations-Angabe.
Man sieht auch, dass i zwar sehr wohl den Wert 10 annahm, für diesen Wert aber der Schleifendurchlauf übersprungen wurde – daher Skipping.
Solche Bedingungen sind nicht nur bei Stepped control erlaubt, sondern auch bei den neuen Formen der Iterationskontrolle, die in weiteren beschrieben werden.
Single Expression Iteration Control
Eine komplett neue Form der Iteration ist die Single Expression Iteration Control, die für sich allein genommen erst einmal sinnfrei erscheint, aber im Zusammenhang mit REPEAT ganz neue Möglichkeiten erschließt:
BEGIN
FOR i IN 2, REPEAT POWER(i,2) WHILE i < 300 LOOP
DBMS_OUTPUT.put_line(i);
END LOOP;
END;
/
2
4
16
256
Die Schleife kann also nicht mehr nur in gleichbleibenden Schritten durchlaufen werden wie bisher; stattdessen kann in der REPEAT-Klausel ein Ausdruck angegeben werden, der von Durchlauf zu Durchlauf die Schrittgöße verändert.
Syntaktisch erlaubt ist es, REPEAT wegzulassen – aber wenig sinnvoll. Für einen einzelnen Wert braucht man schließlich keine Schleife.
REPEAT ist auch ohne die WHILE-Bedingung erlaubt, aber dann sollte man tunlichst innerhalb der Schleife für eine Abbruchbedingung sorgen!
Klassischerweise durchläuft man Arrays in einer FOR-Schleife über die Array-Methoden COUNT oder, alternativ, FIRST / LAST, wobei man bei Lücken im Array entsprechende Zusatzbedingungen einbauen muss, wie z. B. die Abfrage auf EXISTS:
DECLARE
TYPE T IS TABLE OF VARCHAR2(20);
V_ARR T;
BEGIN
V_ARR := T('EINS','ZWEI','DREI','VIER');
FOR I IN 1..V_ARR.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(V_ARR(I));
END LOOP;
END;
/
EINS
ZWEI
DREI
VIER
Jetzt gibt es drei neue Formen der Iterationskontrolle, die den Umgang mit Arrays innerhalb der Schleife deutlich vereinfachen können.
VALUES
Mit VALUES werden die Werte eines Arrays ausgegeben, egal, ob Zahl oder String, und es führt noch nicht einmal zu einem Fehler, wenn das Array Lücken hat.
Das Array selber muss in der Schleife gar nicht mehr angesprochen werden, der jeweilige Wert steht im Iterator.
DECLARE
TYPE T IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
V_ARR T;
BEGIN
V_ARR := T('EINS','ZWEI','DREI','VIER');
V_ARR.delete(2);
FOR I IN VALUES OF V_ARR LOOP
DBMS_OUTPUT.PUT_LINE(I);
END LOOP;
END;
/
EINS
DREI
VIER
Diese Syntax ist nicht nur für Collections zulässig, sondern in ähnlicher Form auch für Cursor und Selects. Darauf soll an dieser Stelle aber nicht weiter eingegangen werden. Beispiele dazu finden sich in der Doku.
INDICES
Das Gegenstück zu VALUES ist INDICES. Statt der Werte werden die vorhandenen Indices des Arrays durchlaufen:
DECLARE
TYPE T IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
V_ARR T;
BEGIN
V_ARR := T('EINS','ZWEI','DREI','VIER');
V_ARR.delete(2);
FOR I IN INDICES OF V_ARR LOOP
DBMS_OUTPUT.PUT_LINE(I);
END LOOP;
END;
/
1
3
4
PAIRS
Beides kombinieren lässt sich mit PAIRS:
DECLARE
TYPE T IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
V_ARR T;
BEGIN
V_ARR := T('EINS','ZWEI','DREI','VIER');
V_ARR.delete(2);
FOR i,j IN PAIRS OF V_ARR LOOP
DBMS_OUTPUT.PUT_LINE(i || '=>'|| j);
END LOOP;
END;
/
1=>EINS
3=>DREI
4=>VIER
Allen drei neuen Collections-Control-Typen gemein ist, dass sie sogar mit nicht initialisierten Arrays klarkommen. Der folgende Code führt nicht zu einem Fehler:
DECLARE
TYPE T IS TABLE OF VARCHAR2(20);
V_ARR T;
BEGIN
FOR I IN VALUES OF V_ARR LOOP
DBMS_OUTPUT.PUT_LINE(I);
END LOOP;
END;
/
Zum Vergleich:
DECLARE
TYPE T IS TABLE OF VARCHAR2(20);
V_ARR T;
BEGIN
FOR I IN 1..V_ARR.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(V_ARR(I));
END LOOP;
END;
/
ORA-06531: Nicht initialisierte Zusammenstellung referenziert
Die bisher so übersichtliche und über viele Versionen hinweg unveränderte FOR-Schleife ist im Syntax-Graph kaum wiederzuerkennen. Was davon in der Praxis Einzug hält und was eher Spezialfällen vorbehalten ist, wird sich zeigen - und hängt natürlich immer von der jeweiligen Aufgabenstellung ab. Jedenfalls haben sich die Möglichkeiten enorm erweitert.
In unserer Know-How Datenbank finden Sie mehr als 300 ausführliche Beiträge zu den
Oracle-Themen wie DBA, SQL, PL/SQL, APEX und vielem mehr.
Hier erhalten Sie Antworten auf Ihre Fragen.