Steuerung des Programmfluß

Wie in anderen Programmiersprachen auch gibt es im wesentlichen zwei Konstrukte für die Steuerung des Programmfluss.

  • Konditionale Konstrukte (Bedingungen)
  • Schleifenkonstrukte
    Natürlich existieren neben diesen auch das ungeliebte GOTO und eine Reihe von Modularisierungstechniken, die die Wartbarkeit des Codes erhöhen. Ausserdem existieren in PL/SQL Programmen Besonderheiten in der Arbeit mit Exceptions. All dies gehört allerdings nicht klassisch in die Programmflußsteuerung und wird daher hier noch nicht behandelt.

IF-THEN

Das Format des IF THEN Statements ist wie folgt:

IF <condition> THEN 
  executable statements; 
END IF;

Die Bedingung wird ausgewertet. Wenn die Bedingung wahr ist, werden die ausführbaren Anweisungen ausgeführt. Wenn die Bedingung falsch oder NULL ist, werden die ausführbaren Anweisungen übersprungen.

IF-ELSIFELSE

Das ELSIF ist kein typografischer Fehler. Das Format des IF-ELSIF Statement ist wie folgt:

IF <condition1> THEN
  <statements1> 
ELSIF <condition2> THEN
  <statements2> 
ELSIF <condition3> THEN   
  <statements3> 
ELSE   
  <statementsN> 
END IF;

Das IF-ELSIFELSE Statement kann begriffen werden als viele IF-THEN Statements, von denen nur eine wahr sein kann. Tatsächlich werden die folgenden Bedingungen nicht mehr ausgewertet, sobald die erste wahr ist. Ist keine der Bedingungen wahr, wird der ELSE-Teil ausgeführt.

Verschachtelte IF Statements

Es ist möglich, mehrere IF Statements zu verschachteln. Als Faustregel gilt, wenn mehr als drei Verschachtelungstiefen vorliegen, sollte man seinen Code kritisch betrachten. Grundsätzlich ist die Verschachtelungstiefe unendlich, praktisch hängt sie von den Serverressourcen ab.

Das GOTO Statement

Eigentlich wollen wir gar nicht wissen, dass es das GOTO Statement gibt, denn durch seine Verwendung lässt sich herrlich verwirrender Programmcode erzeugen. Nun, obwohl sich beweisen lässt, dass man in der Programmierung ohne die verpönte Sprunganweisung auskommt, sei sie hier dennoch erwähnt. Als Sprungziel nutzt man sogenannte Labels. Labels spielen neben einem Sprungziel auch eine Rolle für die Sichtbarkeit von Variablen in verschachtelten Blöcken.

Beispiel:

DECLARE  
  a number :=0; 
BEGIN 
  <<label1>>  
  a := a + 1;  
  if a < 10 then
     goto label1;  
  end if; 
END;

Neben konditionalen Konstrukten gibt es noch Schleifenkonstrukte

Schleifenkonstrukte

Die Fähigkeit, Schleifen zu formulieren ist einer der Hauptunterschiede zwischen SQL und PL/SQL, so dass wir diesen Unterschied sehr genau betrachten wollen. Schleifenkonstrukte sind recht einfach und leicht zu verstehen.

  • Einfache Schleifen
  • Numerische Schleifen
  • WHILE-Schleifen

Warum gibt es drei verschiedene Konstrukte. Für Flexibilität. Nun, ich nutze trotzdem meist nur eine Form.

In der Literatur werden Sie noch von einer weiteren Schleifenform lesen, der CURSORFORLOOP. Diese Schleife ist ein sehr mächtiges Konzept, auf das wir aber erst eingehen im Bereich der Datenbank-Interaktion.

Die einfache Schleife

Die einfache Schleife wird auch als Endlos-Schleife bezeichnet. Warum das der Fall ist, sehen wir gleich. Die Syntax für die einfache Schleife ist wie folgt:

LOOP   
  executable statements;   
  EXIT WHEN (condition); 
END LOOP;

Versuchen wir ein Beispiel: Geben Sie in SQL*Plus folgendes ein:

set serveroutput on 
DECLARE i 
  NUMBER := 1; 
BEGIN
   dbms_output.enable(100000);   
  LOOP     
  dbms_output.put_line(i);     
  i := i + 1;     
  EXIT WHEN i > 10;   
  END LOOP; 
END;

Wenn Sie diesen Code ausführen, sehen Sie die Zahlen 1 bis 10 auf dem Bildschirm. Der Schleifencode wird solange ausgeführt bis die EXIT WHEN Bedingung wahr wird. Der Aufruf von DBMS_OUTPUT erzeugt Bildschirmausgaben, darum kümmern wir uns später. Diese Schleifenform heißt auch rumpfgesteuerte Schleife, denn sie wird mindestens einmal ausgeführt. Ob weitere Durchläufe erfolgen, entscheidet sich innerhalb der Schleife (also im Rumpf) mit Hilfe der EXIT WHEN Bedingung. Fehlt diese oder wird niemals wahr, haben Sie eine Endlosschleife produziert, die niemals terminiert.

Ein klassischer Fehler

DECLARE    
  i NUMBER; 
BEGIN   
  dbms_output.enable(100000);   
  LOOP     
    dbms_output.put_line(i);     
    i := i + 1;     
    EXIT WHEN i > 10;   
  END LOOP; 
END;

Nun, sieht ja fast aus wie das vorige Beispiel, ist aber eine Endlosschleife. Wo liegt das Problem? Wir haben versäumt, die Variable i mit einem Wert zu initialisieren. Der Variableninhalt von i ist im ausführbaren Teil also NULL, unbekannt. Selbst wenn wir i um eins erhöhen: unbekannt erhöht um eins ist? Unbekannt! Die EXIT WHEN Bedingung wird daher niemals wahr, also terminiert die Schleife nicht.

Die numerische Schleife

Numerische Schleifen führen den Schleifenrumpf eine feste Anzahl Male aus, die Häufigkeit wird mit dem Schleifenindex festgelegt. Die Syntax für numerische Schleifen ist:

FOR loop_index IN lower_range .. upper_range LOOP
   executable statements 
END LOOP;

Beispiel

set serveroutput on 
BEGIN   
  dbms_output.enable(10000);   
  FOR i IN 1 .. 10   LOOP     
    dbms_output.put_line(i);   
  END LOOP; 
END;

Der Code wird zehnmal ausgeführt. Es demonstriert also eine andere Form der einfachen Schleife. Die numerische Schleife wird genutzt, wenn Sie die Anzahl Schleifendurchläufe kennen und Sie die Schleife nicht bereits vorher abbrechen wollen. Erwähnt sei folgendes: Die Syntax min .. max ist sehr selten, es handelt sich nicht um einen Schreibfehler! Der Schleifenindex i muß nicht deklariert werden! Innerhalb des Schleifenrumpfs darf keine Wertzuweisung an i erfolgen. Schleifen zählen nur ganzzahlig und stets mit dem Inkrement (oder Dekrement) 1.

Es ist also auch möglich, rückwärts zu zählen:

set serveroutput on 
BEGIN   
  dbms_output.enable(10000);   
  FOR i IN REVERSE 1 .. 10   LOOP     
    dbms_output.put_line(i);   
  END LOOP; 
END;

Diese Schleife zählt von zehn bis eins rückwärts. Wichtig ist, dass Sie nicht die Ober- und Untergrenze des Intervalls einfach umdrehen, sondern das Schlüsselwort REVERSE nutzen. Im Gegenteil, wenn die Untergrenze (links) größer als die Obergrenze (rechts) ist, wird die Schleife gar nicht ausgeführt. Es handelt sich hier also nicht um eine rumpfgesteuerte, sondern eine sogenannte kopfgesteuerte Schleife: Ob ein Schleifendurchlauf erfolgt oder nicht, wird vor Schleifeneintritt geprüft. Der Grund für die Nutzung von Schleifen liegt nun weniger in der Anzeige von Zahlen, sondern vielmehr, um bspw. Datensätze zu bearbeiten, die aus der Datenbank “gefetcht” werden. Hierfür können wir entweder die einfache Schleife oder die sogenannte Cursor-Schleife nutzen (den Cursor werden wir später behandeln).

Hier ein Beispiel mit der einfachen Schleife

DECLARE 
  CURSOR c_students IS   SELECT * FROM students;   
  student_rec c_students%rowtype; 
BEGIN   
  OPEN c_students;   
  LOOP     
    FETCH c_students INTO student_rec;     
    EXIT WHEN c_students%notfound;   
  END LOOP;   
  CLOSE c_students; 
END; 
/

Hier geschehen mehrere spannende Sachen: Im DECLARE Teil wird eine zusammengesetzte Variable deklariert, ein sogenannter Record, dessen Aufbau sich aus dem Rückgabewert des Cursors ableitet. Für den Datentypen verwendet man hierbei die verankerte Methode und bezieht sich mit %ROWTYPE auf den Aufbau z.B. einer Tabelle oder aber eines Cursors. In der LOOP Schleife wird nun der gesamte Datensatz aus dem Cursor nicht in einzelne Variablen gestellt, sondern in den Record, der ja den korrekten Aufbau hat. Dazu später mehr. Nach dem FETCH überprüfen wir, ob noch ein Datensatz im Cursor zur Verfügung steht. Wenn nicht, wurden also alle verarbeitet, wir verlassen die Schleife und schließen den Cursor.

Die WHILE-Schleife

Die WHILE-Schleife wird solange ausgeführt wie eine Testbedingung wahr ist.

set serveroutput on 
DECLARE   
  i NUMBER := 0; 
BEGIN   
  dbms_output.enable(1000);   
  WHILE i < 10   LOOP     
     i := i + 1;     
    dbms_output.put_line(i);   
  END LOOP; 
END;

Die WHILE Schleife wird genutzt, wenn nicht bekannt ist, wie häufig die Schleife ausgeführt werden muß und die Durchführung der Schleife unter Umständen gar nicht nötig ist. Denn ist die WHILE-Bedingung beim Schleifeneintritt bereits unwahr, wird die Schleife gar nicht ausgeführt.

Die FORCURSORLOOP

Hier ein anderes Beispiel für die Verarbeitung von Datensätzen. Wir führen jetzt die sogenannte FORCURSORLOOP ein:

DECLARE   
  CURSOR c_students IS   SELECT * FROM students; 
BEGIN   
  FOR c_students_rec IN c_students LOOP
    NULL;   
  END LOOP; 
END; 
/

Nun, das ist die Schleifenvariante mit dem wenigsten Tippaufwand. Was passiert. Im FORLOOP Konstrukt erfolgt bereits das FETCH aus dem Cursor in den Record. Wir brauchen kein Ende-Kriterium, die Schleife terminiert, wenn keine Datensätze mehr im Cursor zur Verfügung stehen. Besonderheit: Der Cursor muß weder geöffnet noch geschlossen werden, das geschieht automatisch. Weitere Besonderheit: Der Record c_students_rec muß nicht deklariert werden. Auch dies geschieht automatisch. Warum hat die erste Variante dennoch ihre Existenzberechtigung, wenn es doch auch einfacher geht? Die einfache Schleife ist dann zu bevorzugen, wenn der Schleifenabbruch bereits erfolgen kann, ohne alle Datensätze verarbeitet haben zu müssen. Das hier vorgestellte Konstrukt verarbeitet erst alle Datensätze, dann terminiert die FORCURSORLOOP.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.