00:00:00,016 --> 00:00:25,433 Hallo, mein Name ist Jens Lambert. In diesem Video zeige ich Ihnen, wie Sie mit dem SQL Developer von Oracle PL/SQL Anweisungen und Programme schreiben, bearbeiten und ausführen können. Außerdem werden wir uns ansehen, wie im SQL Developer bei der Fehlersuche vorgegangen werden kann. Als Beispiel verwenden wir die Umsetzung eines einfachen Kassensystems eines Möbelkaufhauses. 00:00:25,433 --> 00:01:49,648 Wir beginnen mit einer geöffneten Session an einem Oracle Datenbank Server. Die Konsolen Ausgabe kann auf zwei verschiedene Arten aktiviert werden. Zum Testen führen wir eine einzige Anweisung mit EXECUTE aus. Diese soll auf der Konsole den Text "Hallo Welt" ausgeben. Allerdings sehen wir nur, dass die Anweisung ausgeführt wurde. Was ausgeführt wurde, wird nicht angezeigt. Um die Konsolen Ausgabe für die verwendete Session zu aktivieren, kann der Skript Befehl SET SERVEROUTPUT ON dem ersten Befehl vorangestellt werden. Nun wird die Ausgabe auch in der Konsole angezeigt. Als Alternative bietet der SQL Developer auch die Möglichkeit an, einen zusätzlichen Bereich für die Ausgabe von Meldungen des Servers anzulegen. Dazu findet man im Menü Ansicht den Eintrag DBMS Ausgabe. Im neu angezeigten Bereich muss dann mit dem grünen Plus Symbol eine Verbindung zur verwendeten Datenbank hergestellt werden. Die Ausgabe teste ich nun mit einem anonymen Block, der mehrere Anweisungen beinhalten kann. Die Sequenz aus vier Anweisungen erzeugt eine Ausgabe in zwei Zeilen im Ausgabe Bereich. Erst wenn nach mehreren Aufrufen PUT_LINE aufgerufen wird, wird die Zeile abgeschlossen und eine neue Zeile begonnen. 00:01:49,648 --> 00:02:47,031 Für die Arbeit mit PL/SQL habe ich zwei Tabellen vorbereitet, die in vereinfachter Form das Kassensystem eines Möbelkaufhauses darstellen sollen. Es gibt eine Tabelle für die Artikel, in der ein paar Möbel mit Artikelnummer, Bezeichnung und im Netto-Einkaufs- und Verkaufspreis je Stück gespeichert sind. Damit kein Artikel unter dem Einkaufspreis verkauft wird, integriere ich einen passenden Check-Constraint. Jetzt noch ein paar Möbelstücke in die Tabelle eingefügt und fertig ist unser kleiner Möbelkatalog. Um die Verkäufe zu dokumentieren, benötige ich eine Art Kassensystem. Die eindeutige Beleg Nummer, die auch als Primärschlüssel verwendet wird, generiere ich mit einer SEQUENCE. In der Tabelle Verkäufe sollen die verkauften Artikel anhand der Artikelnummer mit ihrer Anzahl registriert werden. Der vom Käufer gezahlte Preis wird auch dokumentiert. 00:02:47,031 --> 00:05:04,996 Bevor etwas verkauft werden kann, interessiert den Kunden natürlich der Verkaufspreis auf dem Preisschild. Weil in der Artikel Tabelle lediglich der Nettoverkaufspreis ausgewiesen wird, benötige ich für die spätere Bezahlung noch eine Funktion, die mir den Verkaufspreis mit Mehrwertsteuer und dem möglichen Rabatt berechnet. Das bereite ich zunächst in einem anonymen Block vor. Zuerst werden unter DECLARE lokale Variablen für den Nettoverkaufspreis und den möglichen Rabatt in Prozent definiert. Außerdem wird noch eine Konstante für die Mehrwertsteuer angelegt. Im ausführbaren Abschnitt brauchen wir eine Abfrage, die uns von der Artikelbezeichnung oder Artikelnummer den zugehörigen Nettoverkaufspreis ermittelt. Dieser wird dann in einer lokalen Variablen gespeichert, mit den Faktoren für Rabatt und Mehrwertsteuer ausmultipliziert und auf zwei Nachkommastellen gerundet ausgegeben. Wird der anonyme Block ausgeführt, kommt es zu einer Fehlermeldung: "ID Nettoverkaufspreis muss deklariert werden." Die Zeilen Nummer des Fehlers bezieht sich hier auf den Fundort des Fehlers ab Beginn der Anweisung oder Markierung. In der SQL-Abfrage gibt es den Nettoverkaufspreis allerdings nur als Attribut einer Relation, nicht als lokale Variable. Im Deklarationsabschnitt finden wir jedoch eine gleichnamige lokale Variable mit dem Präfix l_. Eine Deklaration der Variable hat also stattgefunden, nur wurde hier das Präfix l_ vergessen. Das trage ich nun nach. Ein erneutes Ausführen erzeugt nun eine weitere Fehlermeldung: "Keine Daten gefunden." Anhand der Zeilen Nummer ist erkennbar, dass es sich hier um einen SQL-Abfrage Fehler handelt und die Abfrage ein leeres Ergebnis zurückgibt. Ich korrigiere das, indem ich eine Artikelnummer eintrage, die in der Relation vorhanden ist. Jetzt wird der erwartete Verkaufspreis ausgegeben. Damit dieses Programm leicht von einer Anwendung aufgerufen werden kann, soll es als STORED FUNCTION in der Datenbank als Funktion Verkaufspreis gespeichert werden. 00:05:04,996 --> 00:08:46,978 Nun deklariere ich die Funktion zur Ermittlung des Verkaufspreises mit der Signatur CREATE OR REPLACE FUNCTION Verkaufspreis. Als Übergabe Parameter definiere ich die Artikelnummer mit in_artikelnummer IN NUMBER und den prozentualen Rabatt mit in_rabatt IN NUMBER. Den deklarativen Abschnitt übernehme ich aus dem zuvor erstellten anonymen Block und passe den Rabatt entsprechend an. Dabei fällt auf, dass die lokale Variable für den Nettoverkaufspreis mit einem Syntax Fehler markiert wird. Weil es keine genaueren Angaben zum Fehler gibt, fahre ich zunächst fort. Den Programmcode übernehme ich auch und führe die Funktion zum Test aus. Diesmal wird der Programmcode jedoch nicht wie der anonyme Block ausgeführt, sondern kompiliert und in der Datenbank gespeichert. Dabei werden wir auf den folgenden Fehler hingewiesen: "Fand das Symbol l_nettoverkaufspreis als eines der folgenden erwartet wurde: return." Die angezeigten Zeilen und Spalten Nummern des Fehlers beziehen sich jetzt im "Compiler-Log" auf das SQL-Skript und in der Skript Ausgabe auf den Code in der Datenbank. Wird keine Fehler Beschreibung angezeigt, kann diese mit SHOW ERRORS aufgerufen werden. Nach dem Aktualisieren und Öffnen der Funktionen-Rubrik im Datenbank-Explorer wird die Funktion Verkaufspreis mit einem weißen X in einem roten Kreis als Fehler markiert. Mit einem Klick auf die in der Datenbank gespeicherte Funktion wird der Inhalt angezeigt und kann bearbeitet werden. Im Normalfall wird die mit Zeile Spalte angegebene Fehler-Position zusätzlich mit einer roten Markierung gekennzeichnet. Durch die Fehlermeldung ist nun klar, dass hier vor ein RETURN mit dem Typ des Funktions-Rückgabewertes eingefügt werden muss. Im SQL-Skript beseitige ich den Fehler, gebe RETURN NUMBER AS ein und kompiliere erneut. Nach dem Aktualisieren verschwindet die Fehler Markierung an der Funktion. Um sicherzugehen, dass die Funktion fehlerfrei ist, kann im Kontextmenü: "Für Debug kompilieren" angeklickt werden. Ein grüner Punkt auf dem Funktionsnamen zeigt nun an, dass es keine kompiler Fehler mehr gibt. Der Fehler kann auch direkt in der Datenbank korrigiert werden und mit dem Zahnrad Symbol erneut kompiliert werden. Das birgt aber die Gefahr, dass die Korrektur im SQL-Skript vergessen wird und so später durch erneutes ausführen des Skripts verworfen wird. Zum Test der Funktion führe ich eine kurze Verkaufspreisabfrage in einem anonymen Block aus und erhalte erneut eine Fehlermeldung: "PL/SQL: Funktion hat keinen Wert zurückgegeben." Es wird also kein Wert von der Funktion zurückgegeben. Dann sehe ich in der Datenbank gespeicherten Funktion nach und stelle fest, dass das abschließende RETURN fehlt, wo jetzt noch eine Ausgabe aufgerufen wird. Im SQL-Skript ergänze ich an der Stelle RETURN und reduziere die Textausgabe auf die Rückgabe einer Zahl, damit der Übergabe-Typ stimmt und kompilierte erneut. Der Test der Funktion gibt jetzt wie gewünscht den Verkaufspreis aus. Anstatt mit einem anonymen Block kann das Ergebnis nun auch mit einem SQL-Query ausgegeben werden. 00:08:46,978 --> 00:09:37,160 Der Verkauf eines Artikels wird nun mit einer Prozedur abgebildet, die als Parameter die Bezeichnung und Anzahl des verkauften Artikels sowie den gewährten Rabatt übergeben bekommt. Die Beleg Nummer wird von einer SEQUENCE eindeutig generiert. Die Artikelnummer wird anhand der Artikelbezeichnung aus der Artikel Tabelle geholt. Dabei wird das einelementige Ergebnis der Abfrage mit dem Schlüsselwort INTO in die Variable l_artikelnummer gespeichert. Um den Verkauf zu dokumentieren, wird in der Tabelle Verkäufe persistiert. Dazu verwende ich die generierte Belegnummer, die ermittelte Artikelnummer, die übergebene Anzahl und den durch die Funktion errechneten Verkaufspreis pro Stück, der noch mit der Anzahl multipliziert wird, um die Endsumme des Einkaufs zu erhalten. 00:09:37,160 --> 00:10:19,910 Ist diese Prozedur nun kompiliert, können einzelne Artikel verkauft werden. Dazu führe ich für jeden verkauften Artikel die Prozedur verkaufen aus und übergebe ihr die Artikelbeschreibung, die Anzahl und den gewährten Rabatt. Um Verkäufe noch mit einem ROLLBACK stornieren zu können, beende ich den Einkauf mit einem COMMIT. Zum Schluss noch einen Blick auf die in der Datenbank abgelegten Verkäufe. Dazu verwende ich eine Abfrage, in der die Relationen Artikel und Verkäufe mit einem Natural Join verknüpft werden und die erfassten Daten formatiert ausgegeben werden. So sehen wir eine schöne Auflistung aller verkauften Möbel mit dem erzielten Verkaufsergebnis. 00:10:19,910 --> 00:10:22,077 Vielen Dank für's Zusehen. Bis zum nächsten Mal.