Prozeduren erzeugen

Wir wollen jetzt all das, was bisher gelernt wurde, zum Erzeugen einer Prozedur nutzen.

Wir haben zwar schon einmal über das Erzeugen von Prozeduren und Funktionen gesprochen, wollen hier nun aber die Eigenarten dieser benannten PL/SQL-Blöcke näher betrachten.

Beispiel

Unsere Prozedur soll

  • Parameter verarbeiten, die wir beim Aufruf übergeben
  • mit Hilfe des übergebenen Arguments einen Datensatz verändern (UPDATE)

Bevor wir beginnen, brauchen wir noch die Tabelle, deren Datensätze unsere Prozedur verändern soll.

create table farbe
  (f_id number(5),
  f_name varchar2(30),
  f_am_lager number(3));

Jetzt packen wir drei Datensätze hinein

SQL>insert into farbe (f_id, f_name, f_am_lager) values (1,'ROT',3);
SQL>insert into farbe (f_id, f_name, f_am_lager) values (2,'BLAU',1);
SQL>insert into farbe (f_id, f_name, f_am_lager) values (3,'WEISS',0);

Die Aufgabe unserer Prozedur ist es, die übergebene Farbnummer zu nutzen, um den Bestand am Lager um die Menge des verkauft Parameters zu reduzieren. Wenn nicht genügend Farbe am Lager ist, muß eine Fehlermeldung zurückgegeben werden.

Beginnen wir mit dem Rahmen:

create or replace procedure f_verk
                       (i_f_id            IN number,
                        i_verkauft        IN number) is
begin
  null;
end;
/

Wenn wir den obenstehenden Code ausführen, wird die Prozedur erzeugt. Sie tut allerdings nichts.

Das Kommando DESC, mit dem wir uns die Struktur von Tabellen anzeigen lassen können, funktioniert auch mit Prozeduren und Funktionen. In diesem Fall sehen wir die Schnittstelle, also die möglichen Ein- und Ausgabeargumente:

SQL> desc f_verk;
PROCEDURE f_verk
Argument Name Type     In/Out Default?
------------------- -------- ----------------
i_f_ID               NUMBER IN     
i_verkauft             NUMBER IN 
SQL>

Um den Source-Code zu sehen, hilft ein Blick in das Data Dictionary:

Select text from user_source where name = 'F_VERK';

Die hier besprochene Herangehensweise wird auch häufig “stubbing” genannt. Ein Prozess, die einzelnen Teile einer Prozedur Schritt für Schritt hinzuzufügen. Dies macht gleichzeitig das Debuggen sehr viel einfacher.

Nun, da wir den Rahmen gesteckt haben, können wir ein wenig Programmierlogik hinzufügen. Die Prozedur braucht einen Cursor, um die Details aus der Datenbank zu selektieren.

create or replace procedure f_verk
                       (i_f_id IN number,
                        i_verkauft      IN number) is
  cursor c_paint(i_paint_id IN farbe.f_id%type) is
    select f_am_lager
    from   farbe
    where  f_id = i_paint_id;
  l_qty    farbe.f_am_lager%type;
  error_code number := SQLCODE;
  error_mess varchar2(200) := SQLERRM;
  no_stock exception;
  ---------------------
  begin
    if c_paint%isopen then
      close c_paint;
    end if;
    open c_paint(i_f_id);
    fetch c_paint into l_qty;
    if l_qty < i_verkauft then
      raise no_stock;
    end if;
    update farbe
    set    f_am_lager = f_am_lager - i_verkauft
    where  f_id          = i_f_id;
  ---------------------
  exception
    when no_stock then
      raise_application_error(-20001,'Nicht genug am Lager');
    when others then
      dbms_output.put_line(to_char(sqlcode||sqlerrm(sql_code)));
end;
/

Der Code sollte problemlos compiliert werden. Wieder, um den SourceCode zu sehen:

Select text from user_source where name = 'F_VERK';

Betrachten wir die Prozedur noch mal genauer:

  • Der Cursor, den wir verwenden, ist dynamisch deklariert, d.h. wir übergeben dem Cursor einen Parameter, anders als bei statischen Cursorn.
  • Die Variable, in der wir den aktuellen Lagerbestand speichern, ist als verankerte Variable deklariert, es ist verankert mit dem Datentyp der Spalte der Tabelle FARBE.
  • Wir haben eine selbstdefinierte Ausnahme no_Stock deklariert. Sie wird ausgelöst, wenn der Lagerbestand geringer als die zu verkaufende Anzahl Farben ist.
  • Im Ausnahmebehandlungsteil lösen wir den Fehler -20001 mit einer eigenen Fehlermeldung aus, wenn die Ausnahme no_stock vorliegt. Erinnern Sie sich, die Nummern -20001 bis -29999 sind reserviert für benutzereigene Fehlermeldungen.
  • Zu guter Letzt enthält unsere Prozedur einen WHEN OTHERS Handler, dieser reagiert auf alle anderen Fehler, die auftreten können und liefert den Original ORACLE-Fehlercode an den Aufrufer zurück.

Jetzt beginnen wir zu testen: In unserer Tabelle steht

SQL> select * from farbe;
F_ID     F_NAME                        F_AM_LAGER
--------- ------------------------------ --------------
1         ROT                            3
2         BLAU                           1
3         WEISS                          0 
SQL>

Zuerst wollen wir einen Eimer roter Farbe verkaufen

SQL> execute f_verk(1,1);
PL/SQL procedure successfully completed.
SQL> select * from farbe;
F_ID     F_NAME                        F_AM_LAGER
--------- ------------------------------ --------------
1         ROT                            2
2         BLAU                           1
3         WEISS                          0
SQL>

Alternativ zum direkten Ausführen aus SQL*Plus können wir den Aufruf der Prozedur natürlich auch aus einem anderen PL/SQL-Block vornehmen

SQL> begin
2 F_VERK(1,1);
3 end;
4 /
PL/SQL procedure successfully completed.
SQL> select * from farbe;
F_ID     F_NAME                        F_AM_LAGER
--------- ------------------------------ --------------
1         RED                            1
2         BLUE                           1
3         WHITE                          0

Nun, da wir zwei Eimer roter Farbe verkauft haben, probieren wir

SQL> execute f_verk(1,2);

Ergebnis:

SQL> execute f_verk(1,2);
begin f_verk(1,2); end;
*
ERROR at line 1:
ORA-20001: Nicht genug am Lager
ORA-06512: at "BULLI.F_VERK", line 28
ORA-06512: at line 1
SQL> select * from farbe;
F_ID     F_NAME                        F_AM_LAGER
--------- ------------------------------ --------------
1         ROT                            1
2         BLAU                           1
3         WEISS                          0 
SQL>

Wir sehen, dass unsere benutzerdefinierte Ausnahme ausgelöst wurde und die Farb-Tabelle nicht verändert wurde.

Übergabe-Argumente

Wie in vorangegangenem Beispiel zu sehen, gibt es offensichtlich verschiedene Varianten der Argumentübergabe.
In unserer Beispielprozedur wurden alle Argumente als IN-Argumente bezeichnet. Dies entspricht dem Default, hätte deshalb auch weggelassen werden können. Für Argumente gilt generell, dass sie in ihrer Größe nicht beschränkt werden dürfen. Also nicht VARCHAR2(20), sondern lediglich VARCHAR2.

Für IN-Argumente gilt, dass sie innerhalb der Prozedur nicht als Zuweisungsziel verwendet werden dürfen. Sie können also nicht verändert werden.
Die Alternative ist ein OUT-Argument: Der übergebene Wert wird dabei nicht berücksichtigt, sondern lediglich durch Zuweisung zurückgeliefert.
Wollen Sie ein Argument sowohl übergeben wie auch verändert zurückliefern, nutzt man IN OUT bei der Argument Spezifikation. Dies gilt auch für Stored Functions.

Beispiel:

create procedure increment (x in out number ) is
begin
  x := x + 1;
end;
/

Schreibe einen Kommentar

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