ORA-Funktionen 1 ORA-Funktionen 25 ORA-Funktionen 26 ORA-Funktionen 1 ORA-Funktionen Die ORA-Funktionen stellen den PHP-eigenen Werkzeugkasten vor, um auf die OracleDatenbank zuzugreifen. Im Handbuch werden sie unter http://de.php.net/manual/de/ref.oracle.php vorgestellt. In der aktuellen Handbuchversion gelten sie als veraltet, weil neben OCI, der Abbildung der Oracle-eigenen Aufrufschnittstelle (Oracle Call API) nun auch PDO (PHP Data Objects) im Rennen um die Gunst des Programmierers sind. Nichtsdestoweniger kann es gerade in PHP 4Anwendungen der Fall sein, dass Lösungen mit ORA-Funktionen zu pflegen sind. Als möglichen Versionen kann man also folgende angeben: PHP 3, PHP 4, PHP 5 bis 5.1.0RC1. 1.1 Referenz Diese Referenz fasst neben den Funktionen auch einige Angaben zur Konfiguration und zu den Konstanten, die als Übergabe- und Rückgabeparameter genutzt werden, zusammen. 1.1.1 Konfiguration In der PHP.ini-Datei müssen folgende Daten eingetragen werden, sofern nur mit einer einzigen Datenbank interagiert werden soll. Ansonsten müssen die entsprechenden Werte dynamisch über PHP mit Hilfe der Funktion void putenv ( string setting ) gesetzt werden. [Oracle] ORACLE_HOME = C:/Programme/Oracle ORACLE_SID = KurseNRW Zusätzlich muss die folgende DLL als Erweiterung frei geschaltet und vor allen Dingen im Erweiterungsordner auch tatsächlich verfügbar sein. extension=php_oracle.dll 27 ORA-Funktionen 1.1.2 Funktionen Die ORA-Funktionen lassen sich in verschiedene Gruppen einteilen, welche die Arbeitsbereiche Verbindungsverwaltung, Cursor-Steuerung oder Abfrageverarbeitung umschließen. 1.1.2.1 Cursor-Steuerung ora_bind(): Der in einer SQL- oder PL/SQL-Anweisung stehende Parameter wird an eine PHP-Variable gebunden. Die allgemeine Syntax lautet: bool ora_bind ( resource Cursor, string PHP-Variablenname, string SQL-Parametername, int Länge [, int Typ] ) ora_close(): Schließt einen Oracle-Cursor. Die allgemeine Syntax lautet: bool ora_close ( resource Cursor ) ora_open(): Öffnet einen Oracle-Cursor für die Ausführung einer Abfrage. Die all- gemeine Syntax lautet: resource ora_open ( resource Verbindung ) ora_parse(): Analysiert eine SQL-Anweisung, die im nächsten Schritt ausgeführt werden soll. Die allgemeine Syntax lautet: bool ora_parse ( resource Cursor, string SQL-Anweisung [, int aufschieben] ) ora_do(): Erleichter die Ausführung einer Abfrage und kombiniert die Funktionen ora_parse(), ora_exec() und ora_fetch(). Die allgemeine Syntax lautet: resource ora_do ( 28 ORA-Funktionen resource Verbindung, string Abfrage ) ora_exec(): Führt eine zuvor analysierte SQL- oder PL/SQLAnweisung an einem Oracle-Cursor aus. Die allgemeine Syntax lautet: bool ora_exec ( resource Cursor ) 1.1.2.2 Ergebnisabruf ora_fetch_into(): Überträgt eine Zeile der durch eine SQL-Abfrage erzeugten Ergebnismenge in ein Array. Die allgemeine Syntax lautet: int ora_fetch_into ( resource Cursor, array &Ergebnis [, int Flags] ) Für die Optionen sind zwei mögliche Werte vorhanden: ORA_FETCHINTO_NULLS sorgt dafür, dass auch NULL-Felder in das Array übernommen werden. Gerade bei mehreren Spalten ist dies wichtig, damit bei einer automatischen über foreach-gesteuerten Verarbeitung Spaltenzuordnungen fehlschlagen. ORA_FETCHINTO_ASSOC sorgt dafür, dass die Ergebniszeile in ein assoziatives Array übertragen wird, in dem die Spaltennamen zu Array-Schlüsseln werden. ora_fetch(): Ruft einen Datensatz einer Ergebnismenge ab. Die allgemeine Syntax lautet: bool ora_fetch ( resource Cursor ) ora_getcolumn(): Liefert die Daten einer abgerufenen Spalte. Die allgemeine Syn- tax lautet: string ora_getcolumn ( resource Cursor, int Feld ) 29 ORA-Funktionen 1.1.2.3 Metadaten der Ergebnismenge ora_columnname(): Liefert den Namen einer im Rahmen einer SQL-Abfrage abgerufenen Spalte. Dies ist entweder der in der Datenbank gespeicherte Spaltenname oder der in der Abfrage vergebene Aliasname. Die allgemeine Syntax lautet: string ora_columnname ( resource Cursor, int Feld ) ora_columnsize(): Liefert die Anzahl der Spalten einer Abfrage. Die allgemeine Syntax lautet: int ora_columnsize ( resource Cursor, int Feld ) ora_columntype(): Liefert den Datentyp einer Spalte aus einer Abfrage. Die allge- meine Syntax lautet: string ora_columntype ( resource Cursor, int Feld ) Mögliche Werte sind die folgenden Datentypnamen: VARCHAR2 und VARCHAR: Zeichenketten variabler Länge CHAR: Zeichenkette fester Länger NUMBER: Ganzzahl oder Dezimalzahl LONG und LONG RAW: Unstrukturierte Binärobjekte ROWID: der Primärschlüssel einer Spalte, der vom System vergeben wird. DATE:Datum CURSOR: Cursor 30 ORA-Funktionen ora_numcols(): Ermittelt die Anzahl der Spalten in einer Ergebnismenge. Die allgemeine Syntax lautet: int ora_numcols ( resource Cursor ) ora_numrows(): Ermittelt die Anzahl der Datensätze in einer Ergebnismenge. Die allgemeine Syntax lautet: int ora_numrows ( resource Cursor ) 1.1.2.4 Transaktionssteruerung ora_rollback(): Setzt eine Transaktion zurück und löscht damit alle Änderungen eines Benutzers nach der letzten Bestätigung. Die allgemeine Syntax lautet: bool ora_rollback ( resource Verbindung ) ora_commit(): Bestätigt und beendet eine Transaktion, d.h. DB-Änderungen werden nun festgeschrieben und sind für alle Benutzer sichtbar. Die allgemeine Syntax lautet: bool ora_commit ( resource Verbindung ) ora_commitoff(): Schaltet die automatische Bestätigung von Transaktionen ab, d.h. Änderungen sind nur für den die Abfrage ausführenden Benutzer sichtbar und werden sofort wieder zurückgerollt. Die allgemeine Syntax lautet: bool ora_commitoff ( resource Verbindung ) ora_commiton(): Schaltet die automatische Bestätigung ein, d.h. Änderungen sind sofort für alle Benutzer sichtbar und werden in der DB festgeschrieben. Die allgemeine Syntax lautet: bool ora_commiton ( resource Verbindung ) 1.1.2.5 Verbindungssteuerung ora_logoff(): Schließt eine bestehende Oracle-Verbindung. Die allgemeine Syntax lautet: bool ora_logoff ( resource Verbindung ) ora_logon(): Erstellt eine neue Oracle-Verbindung. Die allgemeine Syntax lautet: resource ora_logon ( 31 ORA-Funktionen string Benutzername, string Passwort ) ora_plogon(): Erstellt eine persistente Oracle-Verbindung. Die allgemeine Syntax lautet: resource ora_plogon ( string Benutzername, string Passwort ) 1.1.2.6 Fehlerabruf ora_error(): Liefert eine Oracle-Fehlermeldung zur letzten ausgeführten DB-Aktion. Die allgemeine Syntax lautet: string ora_error ( [resource Cursor_oder_Verbindung] ) ora_errorcode(): Liefert die Oracle-Fehlernummer zur letzten ausgeführten Aktion. Die allgemeine Syntax lautet: int ora_errorcode ( [resource Cursor_oder_Verbindung] ) 1.1.3 Konstanten Die Konstanten dienen dazu, Parameter oder Datenabrufe mit Optionswerten zu versehen. Hinter ihnen verbergen sich wie immer Ganzzahlen, die natürlich auch direkt als Zahlwert übergeben werden können. Dies bietet allerdings nicht den Vorteil, dass man im Quelltext einfach erkennen kann, um welche Option es sich handelt, da die Namen solcher Konstanten für gewöhnlich schon einen sehr guten Hinweis darauf geben, welche Aufgabe sie erfüllen. Die nachfolgende Liste enthält die Bindeoptionen für eingehende Parameter. ORA_BIND_INOUT (integer): Der Parameter ist ein IN-/OUT-Parameter, d.h. von ihm kann gelesen und in ihm geschrieben werden. Dies ist bei Prozeduren für Rückgabewerte möglich, bei denen der Parameter quasi wie bei Variablenübergabe per Referenz Werte an die aufrufende Umgebung zurückgeben kann. 32 ORA-Funktionen ORA_BIND_IN (integer): Der Parameter ist nur ein IN-Parameter, d.h. er kann nur Werte an Oracle übergeben bzw. man kann von ihm nur lesen. ORA_BIND_OUT (integer): Der Parameter ist nur ein OUT-Parameter, d.h. man kann nur seinen Wert setzen, aber keinen Wert an Oracle übergeben. Die nachfolgende Liste enthält die Bindeoptionen für abgerufene Daten einer Abfrage. ORA_FETCHINTO_ASSOC (integer): Die Ergebnisse einer Datenzeile werde in ein assoziatives Array übertragen, wobei die Spaltennamen oder Aliasnamen zu Schlüsseln im Array werden. ORA_FETCHINTO_NULLS (integer): Sofern NULL-Werte im Ergebnis auftauchen, können diese unterdrückt oder auch in die aufrufende Übergebung übermittelt werden. Bei einem mehrspaltigem Ergebnis, das pro Spalte (ArrayFeld) verarbeitet wird, ist das im Normalfall zwingend, damit bspw. in einer HTML-Tabelle keine Spaltenverschiebungen auftreten. Diese Einstellung erzeugt die entsprechenden leeren Array-Einträge. 1.2 Beispiele Dieser Abschnitt zeigt nun, wie die vielen Funktionen im Zusammenhang für die typischen Aufgaben DB-Verbindung, Abfragebearbeitung, Datenänderung, Prozeduraufruf usw. verwendet werden. 1.2.1 Verbindung zur Datenbank Gerade im PHP-Bereich lässt sich ohne Datenbankzugriff im Regelfall keine interessante Software erstellen. Daher konzentriert sich das erste Beispiel auch darauf, eine solche Verbindung einzurichten und wieder abzubauen. Eine Hilfsfunktion getErrors(), welche eine gültige Verbindung als Parameter benötigt, ist in der Lage, diese Verbindung an verschiede vorgegebene Funktionen weiter zu reichen, um Informationen über mögliche Fehler oder den erfolgreichen Abschluss einer Operation in Erfahrung zu bringen. Diese ORA-Funktionen erwarten grundsätzlich eine gültige Verbindung, wie weiter unten noch einmal beschrieben wird. 33 ORA-Funktionen Diese Funktion und die drei typischen Parameter für Benutzernamen, Passwort und Datenbanknamen sind in einer Datei namens db_properties.inc.php untergebracht, welche von den anderen Skripten eingebunden wird. Damit ist es leicht möglich, für ein anderes System schnell die entsprechenden Parameter für alle Beispiele zu ändern. function getErrors($con){ $errors[] = ora_error($con); $errors[] = substr(strchr(ora_error($con), ":"),2); $errors[] = ora_errorcode($con); return $errors; } $user = "SCOTT"; $password = "TIGER"; $instance = "KurseNRW"; Db_properties.php Mit den Funktion ora_logon() für eine kurzzeitige und ora_plogon() für eine dauerhafte Verbindung kann man sich über einen Verbindungsstring, welcher Benutzername und Datenbankname durch ein At-Zeichen trennt, sowie natürlich das Passwort, bei der Datenbank anmelden. Um die Verbindung zu lösen, verwendet man ora_logoff(), welche eine gültige Verbindung als Parameter erwartet. if ($con = ora_logon($user."@".$instance, $password)){ // if ($con = ora_plogon($user."@".$instance, $password)){ print_r(getErrors($con)); $html .= "<p>Verbindung erstellt..."; if (ora_logoff($con)){ $html .= "...und wieder geschlossen.</p>"; } } 34 ORA-Funktionen else { $html = "Verbindung fehlgeschlagen."; } echo $html; ora_verbindung.php: Verbindung mit der DB einrichten Dieses Skript bringt bei entsprechend konfigurierter Datenbank und sonstigen richtigen Systemeinstellungen die gewünschte Information hervor, dass die DB-Verbindung erfolgreich war. Darüber hinaus sieht man auch, wie die Hilfsfunktion die verschiedenen Ausgaben der ORA-Fehlerfunktionen zu einem Array zusammensetzt, welches die einzelnen Bestandteile der Meldung enthält. Abbildung 1.1: Ausgabe im Browser bei erfolgreicher Verbindung… Die Fehlerfunktionen lassen sich nur für eine gegebene und damit für eine gültige Verbindung verwenden. Sobald die Verbindung also gar nicht erst zu Stande kommt, lassen 35 ORA-Funktionen sich keine Fehler abfangen. Diese betreffen dann ausschließlich das Verbindungsverhalten, welches über den Wahrheitswert ermittelt werden kann. Es wird zwar eine Warnung ausgegeben, die allerdings nicht abgefangen werden kann. Abbildung 1.2: … und bei falschem Datenbanknamen 1.2.2 Abfragen ausführen Um Abfrageergebnisse zu verarbeiten, hat man nicht so viele Möglichkeiten wie bspw. bei MySQL oder bei den anderen Werkzeugkästen OCI und PDO, aber natürlich ist es möglich, über die ORA-Funktionen an die gespeicherten Daten zu gelangen und sie in den üblichen Präsentationsformen wieder auszugeben. Das Ziel ist eine Abfrage, die verschiedene NULL-Felder enthält, was bei der Verwendung von anderen Datenbanksystemen nicht einmal im Ansatz für den PHPProgrammierer interessant sein könnte, bei Oracle hingegen mit einer bestimmten Option zu berücksichtigen ist. Das Ergebnis dieser Abfrage ist im nachfolgenden Quelltext abgedruckt und soll in dieser Form auch in HTML erscheinen. 36 ORA-Funktionen Zeit Bereich Termine Teilnehmer Summe ---- -------------------- ---------- ---------- ---------2001 Datenbanken 13 66 46772 2001 Programmierung 25 140 114038 2002 Datenbanken 23 110 77338 2002 Programmierung 55 267 225839 2003 Datenbanken 55 286 213220 131 656 566631 2001 38 206 160810 2002 78 377 303177 2003 186 942 779851 2003 Programmierung 9 Zeilen ausgewählt. Da für gewöhnlich immer eine Menge Tabellen und Listen aus DB-Ergebnissen heraus erstellt werden sollen, zeigt dieses Beispiel die Funktionen auch gleich innerhalb einer Bequemlichkeitsklasse, welche genau die Erstellung dieser HTML-Objekte vereinfachen soll, wenn es darum geht, einfach nur die Daten zu präsentieren. Die ersten beiden Methoden getUL() und getOL() bieten an, anhand dreier Parameter für den SQL-Quelltext, das Verbindungshandle und den Listen-Untertyp (quadratische Aufzählungszeichen oder römische Buchstaben etc.) eine entsprechende OL- oder ULListe aus einer Abfrage heraus zu erstellen, die nur eine Spalte zurückliefert. Für den eigentlichen Datenbereich, der ja aufgrund der LI-Elemente immer gleich ist, gibt es eine weitere Methode, welche die Daten beschafft und aufbereitet. In PHP 5 würde man diese Methode privat deklarieren, was allerdings in PHP 4 noch nicht geht. Da bei den ORA-Funktionen höchstens PHP 4 im Einsatz ist, soll auch nur dessen Sprache verwendet werden. class oraQuery { /* Ausgabe einer OL-Liste */ 37 ORA-Funktionen function getUL($sql, $con, $type){ $html = "<ol type=\"" .$type ."\">\n" .$this->getListItems($sql, $con) ."</ol>\n"; return $html; } /* Ausgabe einer UL-Liste */ function getOL($sql, $con, $type){ $html = "<ul type=\"" .$type ."\">\n" .$this->getListItems($sql, $con) ."</l>\n"; return $html; } ora_abfrage.php: Methoden zur Listenerzeugung Diese sinngemäß private Methode getListItems() erwartet also den SQL-Quelltext und eine Datenbankverbindung. Die ORA-Funktionen setzen einen so genannten Cursor ein, um dessen Gunst sich verschiedene Verwaltungsfunktionen zum Öffnen, Schließen und natürlich Verarbeiten bemühen. Er dient als Zugriffspunkt für die Daten, die nachher mit Hilfe der ora_fetch()-Funktion abgerufen werden können. Wie auch bei den OCI-Funktionen muss das SQL zunächst geparst werden. Dies bedeutet eine Quelltextanalyse, die auch das Binden von Variablen beinhalten kann, und geschieht über o- 38 ORA-Funktionen ra_parse(), bevor dann der korrekte Cursor bzw. der ihn repräsentierende Quelltext auch tatsächlich unter Einsatz von ora_exec() ausgeführt werden kann. Die Daten schließlich werden automatisch als numerisches Array geliefert, wobei in diesem Fall nur das erste Feld gesetzt ist, da ja nur eine Spalte erwartet wird. Dies erleichtert den Zugriff und erspart eine foreach-Konstruktion. Stattdessen greift man der Reihe nach auf die einzelnen Datenfelder in der ersten Spalte zu und gibt sie in Listenelementen aus. /* Ausgabe von Listeneinträgen */ function getListItems($sql, $con){ // Cursor öffnen $cursor = ora_open($con); // SQL-Anweisung analysieren und ausführen if (ora_parse($cursor, $sql) && ora_exec($cursor)){ // Datenbereich $html = ""; while(ora_fetch($cursor)){ $field = trim(ora_getcolumn($cursor, 0)); // NULL-Felder erkennen if($field != "") { $html .="\t<li>$field</li>\n"; } } // Cursor schließen $cursor = ora_close($cursor); // Rückgabe return $html; } 39 ORA-Funktionen else { return false; } } ora_abfrage.php: ORA-Funktionen zum Datenabruf Nach dieser einfachen Ausgabe folgt nun eine dynamische Tabelle. Sie wird dynamisch genannt, weil sie in Breite und Höhe sich nach den gelieferten Daten richten soll. Dabei sollen auch Spaltenköpfe in TH-Elementen erzeugt werden, die ihre Einträge aus der Ergebnismenge und damit aus den Spalten- oder Aliasnamen der SQL-Abfrage beziehen. Prinzipiell ist natürlich die Erzeugung einer Tabelle aus einer Ergebnismenge, die auf eine Oracle-Datenbank zugreift, sehr mit derjenigen zu vergleichen, die Daten aus einer MySQL-Datenbank formatiert. Zunächst muss die Struktur der Ergebnismenge analysiert werden. Dies bedeutet insbesondere, dass man die Zahl der Spalten weiß, die für die Breite der Tabelle verantwortlich sind. Mit Hilfe der Funktion ora_numcols() kann man diese Information abrufen. Die ORA-Funktionen sind dann besonders einfach zu verwenden, um die Spaltennamen abzurufen. Der Cursor-Zeiger muss nicht eine Zeile nach vorne gesetzt werden; man muss auch nach den erzeugten Spaltenköpfen den Zeiger nicht zurücksetzen; man muss eigentlich wie in Schlaraffia gar nichts an tatsächlicher Arbeit durchführen, sondern lediglich über die ora_columnname()-Funktion, welche den Cursor und die Spaltennummer erwartet, die TH-Elemente ausgeben. Dies Ganze organisiert man durch eine for-Schleife, die wiederum die maximale Spaltenanzahl aufgreift. Für die Erzeugung des Datenbereichs besteht der Quelltext dann aus einem ora_fetch()-Aufruf innerhalb einer while-Schleife, die also so oft iteriert, bis keine Zeilen mehr geliefert werden können. Innerhalb einer Iteration folgt dann, weil es so angenehm war, noch einmal eine for-Schleife, um alle Spalten aufzurufen. In diesem Fall jedoch möchte man nicht den Spaltennamen erfahren, sondern über ora_getcolumn() den Feldwert. Die NULL-Felder werden automatisch abgerufen und berücksichtigt, sodass man sie in geschützte Leerzeichen umwandeln kann, um die 3D- 40 ORA-Funktionen Ränder der Tabelle nicht zu zerstören. Schließlich können Tabelle und Cursor geschlossen werden. /* Ausgabe einer Standardtabelle für eine Abfrage */ function getTable($sql, $con){ // Cursor öffnen $cursor = ora_open($con); // SQL-Anweisung analysieren und ausführen if (ora_parse($cursor, $sql) && ora_exec($cursor)){ // Analysiert Struktur $cols = ora_numcols($cursor); $rows = ora_numrows($cursor); // Ausgabe vorbereiten $html .= "\n<table border=\"1\">\n"; // Spaltenköpfe $html .= "<tr>\n"; for ($i=0; $i<$cols; $i++) { $html .= "\t<th>" .ora_columnname($cursor, $i) ."</th>\n"; } $html .= "</tr>\n"; // Datenbreich while(ora_fetch($cursor)){ $html .= "<tr>\n"; for($i=0; $i < $cols; $i++){ 41 ORA-Funktionen $field = trim(ora_getcolumn($cursor, $i)); // NULL-Felder umwandeln if($field =="") $field = "&nbsp;"; // Datenzelle $html .="\t<td>$field</td>\n"; } $html .= "</tr>\n"; } // Ausgabe beenden $html .= "</table>\n"; } // Cursor schließen $cursor = ora_close($cursor); // Rückgabe return $html; } } ora_abfrage.php: Klasse mit gekapselter Abfrageverarbeitung Auch wenn das Thema Datawarehouse-Abfragen noch später diskutiert wird, so ist auch die für dieses Beispiel verwandte SQL-Abfrage bereits eine solche Abfrage. Sie enthält nämlich nach der GROUP BY-Klausel noch eine ROLLUP-Klausel, welche dazu anregt, für die einzelnen Jahre, die oben ausgewählt werden, Summen zu bilden. Dies wird durchgeführt, indem die Bereiche zusammengefasst werden. Auch für die Listen ergibt sich eine kleine Besonderheit. Dass man mit den beiden Methoden eine gewöhnliche Liste à la Städteliste, Kursliste oder Bereichsliste erzeugen kann, ist sicherlich offensichtlich. Die Abfrage enthält aber verschiedene HTMLElemente, die möglicherweise zunächst Verwirrung stiften, die aber nachher dazu führen, dass nicht nur alle Bereiche ausgegeben werden, sondern diese auch noch als Link erscheinen, welcher von dieser Master- auf eine Detail-Seite verlinkt, die (hier endet das 42 ORA-Funktionen Beispiel allerdings) nun alle Kurse zu diesem Bereich enthalten könnte. Die möglicherweise eigenwillig und noch lange nicht ausgeschöpfte Nutzung der Zeichenkettenverknüpfung ermöglicht das Verwenden der bekannten Methode zur Erzeugung einer LinkLeiste. require_once("db_properties.inc.php"); require_once("ora_abfrage.php"); if ($con = ora_logon($user."@".$instance, $password)){ // Tabelle $sql = "SELECT TO_CHAR(T_Beginn, 'YYYY') AS \"Zeit\", SUBSTR(K_Bereich,1,20) AS \"Bereich\", COUNT(DISTINCT T_Nr) AS \"Termine\", COUNT(TN_Nr) AS \"Teilnehmer\", SUM(B_Preis) AS \"Summe\" FROM termin NATURAL JOIN buchung NATURAL JOIN kurs WHERE K_Bereich IN ('Programmierung', 'Datenbanken') AND T_Beginn > '01.01.2001' GROUP BY TO_CHAR(T_Beginn, 'YYYY'), ROLLUP(K_Bereich)"; $query = new oraQuery(); $html = $query->getTable($sql, $con); // Liste $sql = "SELECT DISTINCT '<a href=\"bereich.php?bereich=' || K_Bereich || '\">' 43 ORA-Funktionen || K_Bereich || '</a>' AS Bereich FROM kurs"; $html .= $query->getUL($sql, $con, "disc"); $html .= $query->getOL($sql, $con, "a"); // Verbindung schließen ora_logoff($con); } else { $html = "Verbindung fehlgeschlagen."; } echo $html; ora_abfrage_test.php: Test der Abfrageklasse Der entstehenden Tabelle sieht man zwar nicht an, dass sie aus einer Oracle-Datenbank stammt, aber dennoch ist das Ergebnis ein voller Erfolg. Insbesondere die drei Summenzeilen sind besonders gut gelungen, weil sie alleine über SQL und nicht über eine komplexe PHP-Programmierung ausgeführt worden sind. 44 ORA-Funktionen Abbildung 1.3: Ausgabe einer Tabelle Die beiden Listen sind ebenfalls in ihrer Art als einfache Aufzählung und als nummerierte Liste gelungen, wobei sie durch den Trick mit der Zeichenkettenverknüpfung auch noch den Vorteil haben, eine Link-Leiste zu sein. Ein Link wird unten in der Statuszeile des Browsers angezeigt. Hier kann man erkennen, wie der Bereichsname auch als Parameterwert übergeben wird. 45 ORA-Funktionen Abbildung 1.4: Ausgabe von Listen / Link-Leisten 1.2.3 Metadaten einer Abfrage In gängigen Anwendungen sind die Metadaten nicht immer wichtig, bzw. einige wenige wie bspw. der Spaltenname sind besonders interessant und übertrumpfen alle anderen Eigenschaften hinsichtlich der Bedeutung. Dabei ist es durchaus möglich, mit Hilfe der Metadaten bspw. die Validierung von Formularwerten durchzuführen oder Formulare dynamisch aufzubauen. Für solche Zielsetzungen sind die Möglichkeiten, welche mit den ORA-Funktionen umgesetzt werden können, etwas begrenzt. Leider ist hier PHP überhaupt nicht so geschwätzig wie andere Programmiersprachen bzw. Techniken wie ADO in .NET oder JDBC für Java. Hier ist es sogar möglich, Primär- und Fremdschlüsselspalten bzw. sogar die referenzierten Tabellen abzurufen. Ausgestattet mit diesen Informationen gelingen dann tatsächlich ganz interessante Algorithmen, denen man auf den ersten Blick gar nicht ansehen würde, dass sie intensiv von den Metadaten Gebrauch machen. 46 ORA-Funktionen Dieses Beispiel nun soll die verschiedenen verfügbaren Funktionen für die ORAFunktionen zeigen. Es entsteht wieder eine allgemeine Bequemlichkeitsfunktion, welche alle über die ORA-Funktionen verfügbaren Informationen in ein Array schreibt und zurückliefert. Als Parameter dieser Funktion soll ein Tabellen- oder Sichtname übergeben werden sowie eine Datenbankverbindung und ein Trennzeichen, mit dessen Hilfe die verschiedenen Einträge pro Spalte im Rückgabearray getrennt werden können. Diese Struktur soll sich dann später im Klientenquelltext mit Hilfe der explode()Funktion wie in ein weiteres Array zerlegen lassen, sofern man nicht mit anderen Zeichenkettenfunktionen die Daten abrufen möchte. Neben der bereits benutzten Funktion ora_columnname(), welche im Rahmen einer allgemeinen Tabellenausgabe die Spaltenköpfe füllen kann, lässt sich der Datentyp der Spalte über ora_columntype() und die Größe (Zeichenkettenlänge, Vorkommastellen einer Zahl) einer Spalte über ora_columnsize() abrufen. Dies alles verpackt die Funktion pro Spalte als Zeichenkette in einem Array und liefert anschließend dieses Array zurück. require_once("db_properties.inc.php"); function getFieldProperties($table, $con, $separator){ // SQL-Abfrage $sql = "SELECT * FROM " .$table ." WHERE ROWNUM = 1"; // Rufe Feld-Informationen ab $cursor = ora_open($con); ora_parse($cursor, $sql) or die("Request failed."); ora_exec($cursor); // Ergebnismenge untersuchen $cols = ora_numcols($cursor); // Rückgabearray vorbereiten $fields = array(); 47 ORA-Funktionen $i = 0; // Eigenschaften speichern for ($i=0; $i<$cols; $i++) { $name = ora_columnname($cursor, $i); $type = ora_columntype($cursor, $i); $length = ora_columnsize($cursor, $i); $fields[$i] = $table .$separator .$name .$separator .$type .$separator .$length; } // Cursor schließen $cursor = ora_close($cursor); // Rückgabe return $fields; } // Test if ($con = ora_logon($user."@".$instance, $password)){ echo "<pre>"; print_r(getFieldProperties("buchung", $con, "|")); echo "</pre>"; ora_logoff($con); 48 ORA-Funktionen } else { echo "Verbindung fehlgeschlagen."; } ora_metadaten.php: Metadaten einer Ergebnismenge ermitteln Das Array enthält die gesamten abrufbaren Feldeigenschaften in einzelnen Zeichenketten, die als Trennzeichen den senkrechten Strich besitzen und über eine Zeichenkettenexplosion entsprechend in weitere Arrays zu zerlegen wären. Array ( [0] => buchung|B_NR|NUMBER|22 [1] => buchung|TN_NR|NUMBER|22 [2] => buchung|T_NR|NUMBER|22 [3] => buchung|B_DATUM|DATE|7 [4] => buchung|B_TZAHL|NUMBER|22 [5] => buchung|B_PREIS|NUMBER|22 [6] => buchung|B_NRANMELDER|NUMBER|22 [7] => buchung|B_STORNODATUM|DATE|7 [8] => buchung|B_STORNOGEBUEHR|NUMBER|22 ) Ausgabe im Browser: Ermittelte Feldeigenschaften Auch wenn die verschiedenen Bibliotheken in PHP bei Weitem nicht die Informationen liefern, die andere Sprachen liefern können und dies auch zurzeit für PDO noch nicht besser geworden ist, so ist natürlich die Nutzung von Metainformationen nicht unmöglich. Oracle-Programmierer oder natürlich auch Administratoren greifen auf das Data Dictionary zurück, d.h. auf die Tabellen, in denen Tabellen-, Spalteneigenschaften und sämtliche Datenbankobjekte mit ihren Eigenschaften gespeichert sind. Diese lassen 49 ORA-Funktionen sich, sofern man die richtige Sicht kennt und die passenden Berechtigungen hat, natürlich über einfache SQL-Abfragen extrahieren. 1.2.4 Datawarehouse-Abfragen verarbeiten Der Begriff Data Warehouse klingt – gerade in Bezug auf eine Großdatenbank wie Oracle – besonders aufregend und ist typischerweise mit bestimmten Datenmodellen und entsprechenden Administrations- und Installationsaufgaben verbunden. Diese sollen und können weder in diesem Abschnitt noch in diesem Buch erläutert werden. Stattdessen soll lediglich das Grundprinzip genutzt werden, in dem man die in ganz gewöhnlichem Oracle SQL vorhandenen Spracherweiterungen zu Standard SQL verwendet, um eigene Abfragen zu dimensionieren. Im Normalfall erkennt man solche Abfragen bspw. in einer Klausur oder in einem Seminarübungsteil immer an den vielen „pro´s“ in der Frage. So würde also die nachfolgende Abfrage auf die Frage „Zeige Anzahl der Kurse und Anzahl der Buchungen pro Bereich, pro Titel, pro Bereich und Titel zusammen und als Gesamtsumme“ antworten können. Es wird dabei ein gedanklicher und je nach Anzahl der PRO-Anforderungen kein grafisch darstellbarer Würfel erzeugt, der auf verschiedene Weise zerschnitten wird. Im einfachsten Fall verwendet man dabei die CUBE-Anweisung wie im nachfolgenden Quelltext. Sie erstellt automatisch alle denkbaren Schnitte und Dimensionen. SELECT SUBSTR(K_Bereich,1,10) SUBSTR(K_Titel,1,10) AS "Bereich", AS "Titel", COUNT(DISTINCT kurs.K_Nr) AS "Kurse", COUNT(B_Nr) AS "Buchungen", GROUPING(K_Bereich) AS "B", GROUPING(K_Titel) AS "T" FROM kurs INNER JOIN termin ON kurs.K_Nr = termin.K_Nr 50 ORA-Funktionen INNER JOIN buchung ON termin.T_Nr = buchung.T_Nr GROUP BY CUBE (K_Bereich, K_Titel); dw_abfragen.sql: Automatische Erzeugung aller Dimensionen Das Ergebnis ist zwar sehr umfangreich, aber deutlich kürzer als eine nicht dimensionierte Ausgabe. Zudem enthält sie analytische Informationen, die ansonsten nur sehr schwer auf Basis von Rohdaten in einem eigenen PHP-Programm ermittelt werden könnten. Die beiden Spalten, welche über die GROUPING-Funktion aufgebaut werden, enthalten dann eine 1, wenn der Wert in der zugehörigen Inhaltsspalte aufgrund der Gruppierung NULL wird. Sofern man keine Sortierung über ORDER BY vornimmt, erhält man hier bereits eine hervorragende Möglichkeit, um die Gruppen einfach voneinander zu unterscheiden. Die Anzahl der Möglichkeiten, verschiedene Werte für die GROUPING-Spalten vorzugeben (z.B. {0,0}, {0,1}, {1,0} und {1,1}) ergibt sich aus den Kombinationsmöglichkeiten dieser beiden Spalten. Bereich Titel Kurse Buchungen B T ---------- ---------- -------- ---------- ------- ----Datenbanke Oracle 6 271 0 0 Datenbanke SQL 1 49 0 0 Grafik 1 103 0 0 Programmie ASP.NET 1 30 0 0 Server MS Exchang 1 29 0 0 Webdesign Dreamweave 3 145 0 0 13 557 0 1 8 384 0 1 ASP.NET 1 30 1 0 Access 3 133 1 0 Fireworks Datenbanke Grafik 51 ORA-Funktionen 79 2835 1 1 85 Zeilen ausgewählt. Ergebnisse einer DW-Abfrage in Dimensionen Natürlich besteht auch die Möglichkeit, die Dimensionen individuell vorzugeben, wenn nicht der komplette Würfel erzeugt wird. Dies gelingt über die Klausel GROUPING SETS. Innerhalb eines Klammerausdrucks gibt man ganz einfach die gewünschten einzelnen oder in weiteren Klammerungen kombinierten Spalten an. Die nächste Abfrage liefert eine Antwort auf die Frage „Wieviel Kurse und Buchungen hat es pro Titel und Jahr zusammen und pro Bereich gegeben“. SELECT SUBSTR(K_Bereich,1,10) AS "Bereich", SUBSTR(K_Titel,1,10) AS "Titel", COUNT(DISTINCT kurs.K_Nr) AS "Kurse", COUNT(B_Nr) AS "Buchungen", TO_CHAR(B_Datum, 'YYYY') AS "Jahr", GROUPING( TO_CHAR(B_Datum, 'YYYY')) AS "J", GROUPING(K_Bereich) AS "B", GROUPING(K_Titel) AS "T" FROM kurs INNER JOIN termin ON kurs.K_Nr = termin.K_Nr INNER JOIN buchung ON termin.T_Nr = buchung.T_Nr GROUP BY GROUPING SETS (K_Bereich, (K_Titel, TO_CHAR(B_Datum, 'YYYY'))) HAVING COUNT(B_Nr) > 40; dw_abfragen.sql: Angabe benötigter Dimensionen 52 ORA-Funktionen Da in der oberen Abfrage weniger Dimensionen nachgefragt werden, müssen natürlich auch weniger Dimensionen entstehen. Dies ist der Fall, wie man insbesondere an den GROUPING-Spalten erkennen kann. Hier werden nicht alle Kombinationsmöglichkeiten ausgeschöpft, sondern nur zwei. Bereich Titel Kurse Buchungen Jahr J B T ---------- ---------- ---------- ---------- ---- --- --- --PHP 3 55 2000 0 1 0 PHP 3 70 2002 0 1 0 PHP 4 96 2003 0 1 0 SQL 1 49 2003 0 1 0 XML 3 48 2003 0 1 0 Oracle 4 41 2001 0 1 0 Oracle 4 72 2002 0 1 0 Oracle 6 128 2003 0 1 0 13 557 1 0 1 Grafik 8 384 1 0 1 Office 6 146 1 0 1 Datenbanke 25 Zeilen ausgewählt. Nach dieser kurzen Einführung soll nun eine Klasse vorgestellt werden, mit der es möglich ist, die verschiedenen Dimensionen einer nicht nach einer Wertespalte sortierten DW-Abfrage so auszugeben, dass einzelne Tabellen entstehen. Es ist also nicht Ziel, überhaupt das Abfrageergebnis auszugeben. Das gelänge mit einer einfachen dynamischen Tabelle ebenfalls. Ziel ist stattdessen, automatisch die einzelnen Gruppen zu erkennen und für jede Gruppe eine neue Tabelle in HTML zu erstellen. Unabhängig von den ORA-Funktionen zeigt dieses Beispiel mehr einen Algorithmus als neue Funktionen. Die erste Ergebnismenge ist die Anzahl der Kurse und Buchungen in der Dimension Bereich, Titel und Kurse in Kombination. 53 ORA-Funktionen Abbildung 1.5: Dimension 1 Die zweite Ergebnismenge ist die Anzahl der Kurse und Buchungen in der Dimension Bereich. Abbildung 1.6: Dimension 2 Die dritte Ergebnismenge ist die Anzahl der Kurse und Buchungen überhaupt. Dies sind dann quasi die Spaltensummen. 54 ORA-Funktionen Abbildung 1.7: Weitere Dimensionen Die Klasse ist ebenfalls für PHP 4 programmiert, da man die ORA-Funktionen normalerweise in PHP 5-Software ohnehin einsetzen wird / würde. Die Eigenschaften teilen sich auf die verschiedenen Anforderungen auf, welche die Klasse zu erfüllen hat, nämlich die SQL-Abfrage und die DB-Verbindung zu speichern, die verschiedenen Gruppen und Titel zu enthalten, sowie das Trennzeichen und die Titel-Tag zu sammeln. Der Konstruktor erlaubt dann die Übernahme von SQL, DB-Verbindung und den Gruppen, welche mit der Methode splitGrouping() wieder getrennt werden, um sie in der entsprechenden Eigenschaft zu speichern. class oraDWQuery{ var $sql; var $con; var $grouping; var $statistics; var $headings; var $separator; var $headingsTag = "h1"; function oraDWQuery($sql, $con, $grouping){ 55 ORA-Funktionen $this->sql=$sql; $this->con=$con; $this->grouping=$grouping; } oraDWQuery: Konstruktor und Eigenschaften Verschiedne Hilfsmethoden erlauben es, die übergebenen Zeichenketten zu trennen. Hier wäre es genauso gut möglich gewesen, von vornherein Arrays zu übergeben. function splitGrouping(){ $dimensions = array(); $dimensions = explode(",", $this->grouping); return $dimensions; } function splitHeadings(){ $titles = array(); $titles = explode("|", $this->headings); return $titles; } oraDWQuery: Hilfsmethoden zur Zeichenkettenaufspaltung Die Hilfsmethode getTitle($k, $titles) erlaubt es, den passenden Titel für eine zu erzeugende Tabelle auszuwählen. Dabei greift man auf das in der zuvor beschriebenen Hilfsmethode erzeugten Titel-Array zu. function getTitle($k, $titles){ return "<" .$this->headingsTag .">" .$titles[$k] 56 ORA-Funktionen ."</" .$this->headingsTag .">"; } oraDWQuery: Hilfsmethoden zur Titelauswahl Die zentrale Methode getTable() ist in der Lage, die gesamte Ausgabe zu erzeugen. Der Algorithmus ist leider nicht besonders einfach. Nachdem die Dimensionen ermittelt worden sind, benötigt man zunächst die Information, wie viele Reihen überhaupt in der Abfrage abgerufen werden. Dann führt man die Abfrage aus und muss sie verarbeiten. Es sollen nur nicht-GROUPING-Spalten tatsächlich ausgegeben werden. Da ja die GROUPING-Spalten nur für die Unterscheidung der einzelnen Dimensionen bzw. Ergebnistabellen notwendig sind, benötigt man als Unterscheidung, ob eine bestimmte Spalte überhaupt in der Ergebnismenge auftauchen soll oder nicht, die Anzahl der GROUPINGSpalten. Dies legt man zusätzlich um die Schleife, welche sich durch die einzelnen Spalten arbeitet. Es ist nicht unbedingt notwendig, die GROUPING-Spalten am Ende der Spaltenliste auszugeben, obwohl dies natürlich bei der Kontrolle der Ergebnisse empfehlenswert ist. Da innerhalb der Schleife stets auf den Spaltennamen abgeprüft wird und man ja vorher die Namen der GROUPING-Spalten weiß, kann man auch dann ihre Ausgabe unterdrücken, wenn sie an anderer Stelle als am Ende der Spaltenliste erscheinen. Ein weiterer zentraler Aspekt des Algorithmus besteht darin, den Gruppenwechsel zu erkennen, welcher durch die verschiedenen Werte in den GROUPING-Spalten angekündigt wird. Sobald ein neues Wertepaar in der als nächstes abgerufenen Datenreihe gefunden wird, befindet man sich an einem Gruppenwechsel und muss den nächsten Titel und eine neue Tabelle ausgeben bzw. die alte Tabelle zunächst schließen. Zusätzlich dürfen nur noch die Spalten abgerufen werden, die zu dieser Dimension passen, d.h. nur die gefüllten Spalten und für welche die zugehörige GROUPING-Spalte den Wert 0 aufweist. Dies erfordert diverse Merker- und Zählervariablen, mit deren Hilfe auf der einen Seite der Gruppenwechsel registriert wird und mit denen auf der anderen Seite auch die korrekte Ausgabe der nächsten Ergebnistabelle gelingt. Der Algorithmus ist allerdings in wesentlichen Bestandteilen überhaupt für die Zielsetzung einer Gruppierung zu nut- 57 ORA-Funktionen zen und damit von PHP aus unabhängig. Er wird lediglich mit einigen besonderen zusätzlichen Bedingungen verknüpft und ist daher besonders komplex. function getTable(){ // Dimensionen ermitteln $dimensions = array(); $dimensions = $this->splitGrouping(); // Reihen in eigener Abfrage zählen $sqlRows = "SELECT COUNT(*) FROM (".$this->sql.") d"; $cursorRows = ora_open($this->con); ora_parse($cursorRows, $sqlRows) or die("Request failed."); ora_exec($cursorRows); $fields = array(); ora_fetch_into($cursorRows, $fields); $rows = $fields[0]; ora_close($cursorRows); // Abfrage-Cursor öffnen und Abfrage durchführen $cursor = ora_open($this->con); ora_parse($cursor, $this->sql) or die("Request failed."); ora_exec($cursor); // Spalten zählen $columns = ora_numcols($cursor); // Dimensionen zählen $numberDim = array(); for ($i=0; $i<$columns; $i++) { // Nicht-GROUPING-Reihen auslassen 58 ORA-Funktionen if (! is_bool(array_search(ora_columnname($cursor, $i),$dimensions))){ $numberDim[] = $i; } } // Merker-Variablen $fieldOld = ""; // GROUPING-Werte des letzten Fetches $fieldNew = ""; // GROUPING-Werte des neuen Fetch $j = 0; // Zähler für Fetch $k = 0; // Zähler für Tabellen-Titel // Output data $fields = array(); // ORA_FETCHINTO_NULLS um NULL-Werte zu übernehmen while(ora_fetch_into($cursor,$fields, ORA_FETCHINTO_NULLS)){ // Erstelle neues Feld für GROUPING-Werte $fieldNew = ""; // GROUPING-Werte ermitteln for ($i = 0; $i < count($numberDim); $i++){ $fieldNew .= $fields[$numberDim[$i]]; } // Bei Wechsel in GROUPING-Werten -> Neue Tabelle if ($fieldNew != $fieldOld){ // Beende Tabelle (nicht beim ersten Mal) if ($fieldOld != "") $html .= "</table>\n"; 59 ORA-Funktionen // Tabellentitel if (! empty($this->headings)){ if ($k == 0){ $titles = array(); $titles = $this->splitHeadings(); } $html .= $this->getTitle($k, $titles); $k++; } // Beginne Tabelle $html .= "\n<table border=\"1\">\n" // Ausgabe Spaltenköpfe ."\t<tr>\n"; if ($j <= $rows){ // Nur NOT-NULL-Felder ausgeben for ($i=0; $i<$columns; $i++) { if (is_bool(array_search(ora_columnname($cursor, $i),$dimensions))){ // Spaltenköpfe von NOT-NULL-Feldern if ($fields[$i] != ""){ $html .= "\t<th>" .ora_columnname($cursor, $i) ."</th>\n"; } } } } 60 ORA-Funktionen // Ende Spaltenköpfe $html .= "</tr>\n"; } // Ausgabe Datenreihen $html .= "<tr>\n"; for($i=0; $i < $columns; $i++){ // GROUPING-Spalten auslasen if (is_bool(array_search(ora_columnname($cursor, $i),$dimensions))){ $field = $fields[$i]; // NOT-NULL-Datenzellen ausgeben if($field !="") $html .="\t<td>".$field."</td>\n"; } } $html .= "</tr>\n"; // Inkrementation $j++; $fieldOld = $fieldNew; } // Ausgabe </table> für letzte Tabelle if ($j == $rows){ $html .= "</table>\n"; } 61 ORA-Funktionen // Cursor löschen und Rückgabewert zurückliefern $result = ora_close($cursor); return $html; } } ora_dw.php: Ermittlung und Erzeugung der verschiedenen Tabellen Die Verwendung der gerade gezeigten Klasse ist im Gegensatz zur Berücksichtigung der verschiedenen Fälle, die für die Ermittlung der Ergebnistabellen notwendig sind, besonders einfach. Wesentlich ist eine SQL-Abfrage, in der für die einzelnen Spalten, die selbst oder in Kombination als Dimension auftreten, auch die passenden mit der GROUPING()-Funktion erzeugten Spalten auftreten. Ebenso wichtig sind passende Titel für alle Spalten, wobei insbesondere die GROUPING-Spaltennamen auch im Konstruktor übergeben werden müssen, damit in der getTable()-Methode genau diese Spalten richtig erkannt werden. Die Tabellentitel müssen dann in einer der Eigenschaft headings übergeben werden. Hier hätte sich auch eine Set-Methode angeboten, welche die Umwandlung in ein Array automatisch durchführt – oder natürlich unmittelbar ein Array als Übergabewert. Die Reihenfolge der Titel muss dann mit der in der Ergebnismenge auftretenden Reihenfolge übereinstimmen. require_once("db_properties.inc.php"); require_once("ora_dw.php"); if ($con = ora_logon($user."@".$instance, $password)){ // SQL $sql = "SELECT SUBSTR(K_Bereich,1,10) SUBSTR(K_Titel,1,10) AS \"Bereich\", AS \"Titel\", COUNT(DISTINCT kurs.K_Nr) AS \"Kurse\", 62 COUNT(B_Nr) AS \"Buchungen\", GROUPING(K_Bereich) AS \"B\", ORA-Funktionen GROUPING(K_Titel) AS \"T\" FROM kurs INNER JOIN termin ON kurs.K_Nr = termin.K_Nr INNER JOIN buchung ON termin.T_Nr = buchung.T_Nr GROUP BY CUBE (K_Bereich, K_Titel)"; // Datenbereich $oraDW = new oraDWQuery($sql, $con, "B,T"); $oraDW->headings = "Bereich - Titel - Kurse - Buchungen | " ."Bereich - Kurse - Buchungen | " ."Titel - Kurse - Buchungen | " ."Titel - Buchungen | "; $oraDW->separator = "|"; $html .= $oraDW->getTable(); // Verbindung schließen ora_logoff($con); } else { $html = "Verbindung fehlgeschlagen."; } echo $html; ora_dw_test.php: Test der Datawarehouse-Klasse Man erhält als Ergebnis die bereits zu Anfang dieses Abschnitts angegebenen BrowserAusgaben, die hier nicht noch einmal wiederholt werden sollen. 63 ORA-Funktionen 1.2.5 Parameterbindung und anonymes PL/SQL Ein wesentliches Thema für die Nutzung von Oracle ist die Möglichkeit, PL/SQL zu nutzen. Dies kann in Form von anonymen Blöcken, Prozeduren, Funktionen und Paketen geschehen. Während Funktion, die sich auch in SQL nutzen lassen, einfach in gewöhnlichen SQL-Anweisungen erscheinen und daher mit ihnen als Zeichenkette/Anweisung zur Datenbank geschickt werden, erfordert die Verwendung von anonymen Blöcken und Prozeduren besondere Aufmerksamkeit. Sofern man PL/SQL beherrscht, lässt sich hier sagen, dass PL/SQL genauso wie gewöhnliches SQL zur Datenbank geschickt werden kann. In Zusammenhang mit der Ausführung von PL/SQL soll auch das Thema Bindevariablen zur Sprache kommen. Es ist gleichfalls für PL/SQL als auch für SQL einsetzbar, wenn in SQL-Anweisungen Bindevariablen erscheinen, um das lästige Zusammensetzen von Zeichenketten mit Berücksichtigung von Hochkommata zu vermeiden. Das nächste Beispiel zeigt also zunächst, wie ein anonymer PL/SQL-Block zur Ausführung gelangt. Die Technik ist relativ einfach: der Block wird einfach in Form einer Zeichenkette wie gewöhnliches SQL erstellt und zur Datenbank geschickt. Eine besondere Fehlerquelle liegt für Anfänger darin, dass schließende Semikolon für PL/SQL zu vergessen, da dies bei SQL-Anweisungen schließlich nicht notwendig ist. Das Beispiel zeigt allerdings auch, wie so genannte Bindevariablen genutzt werden. Dazu setzt man in PL/SQL bzw. SQL (dieses Thema ist hier genauso möglich) einen Doppelpunkt vor den Variablennamen in der Anweisung. Dies erspart es vor allen Dingen, die Zeichenkette mühselig mit Hochkommata zusammenzusetzen, sofern nicht nur Zahlen, sondern Zeichenketten-Werte übergeben werden sollen. Im Fall von reinem SQL gilt das Beispiel ebenfalls. Hier muss man sich nur vorstellen, dass alle anderen Anweisungen bis auf die INSERT-Anweisung gelöscht werden. Diese mit einem Doppelpunkt ausgezeichneten Variablen müssen dann in PHP gebunden werden, d.h. ihr Wert muss gegen eine PHP-Variable ausgetauscht werden. Dies geschieht mit Hilfe der ora_bind()-Funktion. Sie erwartet als Übergabeparameter den erzeugten Cursor, den Namen der Bindevariablen ohne Doppelpunkt, die Länge der Variablen bzw. des Wertes und eine Konstante, welche den Parametertyp benennt. Hier ist wie in PL/SQL zu unterscheiden, ob von diesem Parameter gelesen werden soll (ORA_BIND_IN) oder ob dieser Parameter auch als Schreibziel zur Verfügung steht 64 ORA-Funktionen (ORA_BIND_OUT). Eine letzte Möglichkeit besteht darin, einen Parameter mit Doppelfunktion (ORA_BIND_INOUT) einzurichten. Parameter in UPDATE, INSERT, DELETE oder SELECT sind immer IN-Parameter. Allerdings gibt es auch die Möglichkeit, über die RETURNING-Klausel bei allen Anweisungen außer SELECT geänderte und gelöschte Spaltenwerte unmittelbar wieder abzurufen. Hierbei sind entweder zwei verschieden Bindevariablen zu verwenden, um Lesen und Schreiben abzubilden, oder man setzt auf einen INOUT-Parameter. Gleiche Überlegungen gelten, wenn man eine Prozedur aufruft, weil eine Prozedur neben gewöhnlichen Übergabeparametern, welche Werte an die Prozedur übermitteln, auch die Möglichkeit bieten, Rückgabewerte zu haben, die ebenfalls in Form von OUT-Bindevariablen an die Prozedur übergeben werden. require_once("db_properties.inc.php"); // Verbindung if ($con = ora_logon($user."@".$instance, $password)){ // Abfrage-SQL mit Binde-Variablen $sql = "DECLARE " ."BEGIN " ." SELECT MAX(T_Nr)+1 " ." INTO :t_Nr " ." FROM termin; " ."INSERT INTO termin (T_Nr, K_Nr, T_Ort, T_Beginn, T_Ende) " ." VALUES (:t_nr, :k_nr, :t_ort, " ." TO_DATE(:t_beginn, 'yyyy-mm-dd'), " ." TO_DATE(:t_ende, 'yyyy-mm-dd')); " ."END;"; // Cursor öffnen $cursor = ora_open($con); // Abfrage analysieren ora_parse($cursor, $sql) or die("Request failed."); // Bindevariablen anmelden 65 ORA-Funktionen $t_nr = null; $t_ort = "Essen"; $t_beginn = "2006-02-13"; $t_ende = "2006-02-15"; $k_nr = 1015024; ora_bind($cursor, "t_ort", ":t_ort", 20, ORA_BIND_IN ); ora_bind($cursor, "t_beginn", ":t_beginn", 10, ORA_BIND_IN ); ora_bind($cursor, "t_ende", ":t_ende", 10, ORA_BIND_IN ); ora_bind($cursor, "k_nr", ":k_nr", 10, ORA_BIND_IN ); ora_bind($cursor, "t_nr", ":t_nr", 5, ORA_BIND_OUT); // Abfrage ausführen ora_exec($cursor); // Cursor schließen $cursor = ora_close($cursor); // Cursor öffnen $cursor = ora_open($con); // Lösch-SQL mit Bindevariablen $sql = "DELETE FROM termin WHERE T_Nr = :t_nr "; // Abfrage analysieren ora_parse($cursor, $sql); // Bindevariablen anmelden ora_bind($cursor, "t_nr", // Abfrage ausführen ora_exec($cursor); // Verbindung schließen ora_close($cursor); 66 ":t_nr", 5, ORA_BIND_INOUT); ORA-Funktionen $html = "Eingetragen und gelöscht: ".$t_nr; } else { $html = "<p>Verbindung fehlgeschlagen.</p>"; } echo $html; ora_manipulation.php: Parameterbindung Im Ergebnis erhält man die Information, dass bei der unveränderten TERMIN-Tabelle mit 653 Datensätzen tatsächlich ein weiterer 654. Datensatz eingefügt und wieder gelöscht wurde. Abbildung 1.8: Ausgabe im Browser 1.2.6 Transaktionssteuerung Sofern die Oracle-Datenbank nicht ohnehin schon vorhanden ist und daher die Kombination PHP und Oracle zum Einsatz kommen soll, entscheidet man sich häufig auch aufgrund der Transaktionsfähigkeit für eine Großdatenbank wie bspw. Oracle. Dies bedeutet, dass mehrere zusammen hängende Anweisungen und DB-Operationen als Ganzes ausgeführt werden und nur im vollständigen Erfolgsfall auch in der Datenbank festgeschrieben werden. Sollte allerdings bei einer Anweisung ein Fehler auftreten, 67 ORA-Funktionen dann werden alle im Rahmen dieser Transaktion zusammen gefassten Anweisungen wieder zurückgesetzt. Die beiden SQL-Schlüsselwörter, die in diesem Zusammenhang eine wichtige Rolle spielen und vermutlich den meisten Lesern schon längst bekannt sind, sind COMMIT für die Bestätigung einer oder mehrer Anweisungen in Folge sowie ROLLBACK, mit der die zurückliegenden Anweisungen zurückgesetzt werden können. Die verschiedenen Möglichkeiten, in PHP die Oracle-Datenbank einzusetzen, erlauben immer auch die Nutzung von Transaktionen. Allerdings muss man hier sagen, dass natürlich über die Verwendung der gerade genannten beiden Anweisungen sowie datenbankspezifische Anweisungen wie SET TRANSACTION ohnehin immer auch Transaktionsunterstützung gegeben ist. Die entsprechenden ORA-Funktionen bieten nur eine PHP-Lösung anstelle der SQL-Anweisungen. Die Funktionsweise von Transaktionen an sich wie auch die Funktionsweise der entsprechenden ORA-Funktionen sollen mit einem längeren Beispiel vorgeführt werden. Zunächst schaltet man die automatische Bestätigung aus, die jede einzelne Anweisung als Transaktion versteht und jeweils bestätigt. Diese allgemeine Steuerung gelingt über ora_commiton() und ora_commitoff(). Sie erwarten jeweils eine gültige DBVerbindung. Dann zählt man die Reihen in der BUCHUNG-Tabelle. Der Zufallsgenerator hat hier 2835 Datensätze eingetragen. Danach löscht man die gesamte Tabelle, fragt erneut ab und erhält die Information, dass natürlich 0 Datensätze gespeichert sind. Mit Hilfe von ora_rollback() kann man nun den Löschvorgang wieder zurücksetzen. Ruft man nun noch einmal die gesamten Datensätze ab, erhält man wieder die Zahl 2835. Schließlich schreibt man das Zurücksetzen mit ora_commit() fest und schaltet auch das automatische Bestätigen mit ora_commiton() wieder an. require_once("db_properties.inc.php"); // Verbindung if ($con = ora_logon($user."@".$instance, $password)){ // Automatisches Commit ausschalten ora_commitoff ($con); // Kontrolle $sql = "BEGIN SELECT COUNT(*) INTO :rows FROM buchung; END;"; 68 ORA-Funktionen // Cursor öffnen $cursor = ora_open($con); // Abfrage analysieren ora_parse($cursor, $sql); // Bindevariablen anmelden $rows = null; ora_bind($cursor, "rows", ":rows", 5, ORA_BIND_OUT); // Abfrage ausführen ora_exec($cursor); $html .= "In DB: ".$rows.".<br/>"; // Löschen $sql = "DELETE FROM buchung"; // Abfrage analysieren ora_parse($cursor, $sql); // Abfrage ausführen ora_exec($cursor); // Kontrolle $sql = "BEGIN SELECT COUNT(*) INTO :rows FROM buchung; END;"; // Abfrage analysieren ora_parse($cursor, $sql); // Bindevariablen anmelden $rows = null; ora_bind($cursor, "rows", ":rows", 5, ORA_BIND_OUT); // Abfrage ausführen ora_exec($cursor); $html .= "Nach Löschvorgang in DB: ".$rows.".<br/>"; // Rollback 69 ORA-Funktionen ora_rollback($con); // Erneute Abfrage $sql = "BEGIN SELECT COUNT(*) INTO :rows FROM buchung; END;"; // Abfrage analysieren ora_parse($cursor, $sql); // Bindevariablen anmelden $rows = null; ora_bind($cursor, "rows", ":rows", 5, ORA_BIND_OUT); // Abfrage ausführen ora_exec($cursor); $html .= "Nach Rollback in der DB: ".$rows.".<br/>"; // Verbindung schließen ora_close($cursor); // Commit ora_commit($con); // Automatisches Commit einschalten ora_commiton($con); } else { $html = "<p>Verbindung fehlgeschlagen.</p>"; } echo $html; ora_transaktion.php: Löschen und rücksetzen Im Browser erscheinen bei korrekter Ausführung die oben angekündigten Ausgaben, die über den aktuellen Zustand der malträtierten BUCHUNG-Tabelle Auskunft geben. 70 ORA-Funktionen Abbildung 1.9: Kontrolle und Korrektur 71 ORA-Funktionen 72 OCI-Funktionen 2 OCI-Funktionen 73 OCI-Funktionen 74 OCI-Funktionen 2 OCI-Funktionen Die OCI-Funktionen stellen die von Oracle selbst angebotene Zugriff-API in PHP dar, die den schönen Namen OCI für Oracle Call API trägt. Dass im Handbuch sehr deutlich der Hinweis auf die Oracle 8-Version prangt, darf vor dem Einsatz in keinem Fall nachteilig ins Gewicht fallen. Die Bibliothek wurde zu Zeiten dieser Datenbankversion in PHP aufgenommen und seitdem leider nicht in eine besser klingende Variante mit modernem Anstrich umbenannt. Im Handbuch werden sie unter http://www.php.net/manual/de/ref.oci8.php vorgestellt. Bis vor Auslieferung von PDO (PHP Data Objects) stellt diese Bibliothek die Standard-API dar, um mit der Datenbank zu interagieren. Sobald PDO mit der Funktionalität der Bibliothek aufgeholt hat, wird sich dies sicherlich ändern, sodass auch die OCI-Funktionen ihre Bedeutung für neue PHP-Anwendungen verlieren werden. Als möglichen Versionen kann man also folgende angeben: PHP 3 >= 3.0.4, PHP 4, PHP 5. Die Beispiele wurden mit Oracle 9i und Oracle 10g ausgeführt. 2.1 Referenz Diese Referenz fasst neben den Funktionen auch einige Angaben zur Konfiguration und zu den Konstanten, die als Übergabe- und Rückgabeparameter genutzt werden, zusammen. 2.1.1 Konfiguration In der PHP.ini-Datei müssen folgende Daten eingetragen werden, sofern nur mit einer einzigen Datenbank interagiert werden soll. Ansonsten müssen die entsprechenden Werte dynamisch über PHP mit Hilfe der Funktion void putenv ( string setting ) gesetzt werden. [Oracle] ORACLE_HOME = C:/Programme/Oracle ORACLE_SID = KurseNRW Insgesamt gib es die folgenden Parameter: 75 OCI-Funktionen ORACLE_HOME: Speicherort der Oracle-Programmdateien ORACLE_SID: Globaler Datenbankname Zusätzlich muss die folgende DLL als Erweiterung frei geschaltet und vor allen Dingen im Erweiterungsordner auch tatsächlich verfügbar sein. extension=php_oci8.dll Seit der Version 1.1 dieser Bibliothek lassen sich in der php.ini noch folgende Einstellungen vorgeben: oci8.privileged_connect mit Standardwert 0 und legt fest, ob erweiterte Verbindungspriveligien für die Angaben OCI_SYSOPER oder OCI_SYSDBA gewährt werden sollen. oci8.max_persistent mit Standardwert -1 und legt die Obergrenze für persistente Verbindungen fest. oci8.persistent_timeout mit Standardwert -1 und legt die maximale Dauer für persistente Verbindungen in Sekunden fest. oci8.ping_interval mit Standardwert 60 und legt das Ping-Intervall in Sekunden fest. oci8.statement_cache_size mit Standardwert 20 und legt die Größe des Anweisungscaches fest. oci8.default_prefetch mit Standardwert 10 und legt die Anzahl der vorab abgerufen Ergebnisreihen fest. oci8.old_oci_close_semantics mit Standardwert 0 und aktviert die ehemalige Funktionsweise der Verbindungsbeendigung. 2.1.2 Funktionen Die OCI-Funktionen lassen sich in verschiedene Gruppen einteilen, welche die Arbeitsbereiche Verbindungsverwaltung, Cursor-Steuerung oder Abfrageverarbeitung umschließen. In PHP 5 wurden die Funktionen neu geschrieben und haben daher auch 76 OCI-Funktionen einen neuen Namen erhalten, wobei alle Funktionen und Methoden, die einen Unterstrich enthalten, zur neuen Bibliothek zählen und nur in PHP 5 funktionieren, während die alten Funktionen bis auf ihren Namen syntaktisch gleich genutzt werden können, aber ihre Wirkung nur für PHP 4 entfalten. In der Referenz sind die alten Funktionsnamen jeweils auch angegeben, wobei allerdings der Fokus auf den neuen Funktionen liegt. 2.1.2.1 Collection In der Oracle-Datenbank gibt es drei verschiedene Collection-Typen, die mit den Arrays in PHP verglichen werden können. Sie erlauben die strukturierte Speicherung, Verarbeitung und den Transport von gleichartigen Werten. Für den Arbeitseinsatz muss man hier zwischen zwei Varianten unterscheiden: Entweder erstellt man eine Collection in PHP, um sie dann an Oracle zu übergeben, oder man erhält aus einer Oracle-Anweisung eine solche Collection in PHP zurück. oci_new_collection(): Erstellt eine neue Collection in PHP, die dann bspw. an die Datebank übergeben werden kann. Die allgemeine Syntax lautet: oci_new_collection ( resource connection, string tdo [, string schema] ) OCI-Collection->append(): Hängt ein neues Element an die PHP-Collection an. Die allgemeine Syntax lautet: bool append ( mixed value ) OCI-Collection->assign(): Weist der einen PHP-Collection einen neuen Wert von einer anderen Collection zu. Die allgemeine Syntax lautet: bool assign ( OCI-Collection from ) OCI-Collection->assignElem(): Weist einem gegebenen Element einer PHP- Collection einen Wert zu. Die allgemeine Syntax lautet: bool assignElem ( int index, 77 OCI-Funktionen mixed value ) OCI-Collection->free(): Löscht die Ressource, die für die Collection erzeugt wurde. Die allgemeine Syntax lautet: bool free ( void ) OCI-Collection->getElem(): Liefert ein Collection-Element zurück. Die allgemeine Syntax lautet: mixed getElem ( int index ) OCI-Collection->max(): Liefert die maximale Anzahl an Elementen einer Collection. Die allgemeine Syntax lautet: int max ( void ) OCI-Collection->size(): Liefert die aktuelle Größe einer Collection, die kleiner oder gleich der maximalen Größe ist. Die allgemeine Syntax lautet: int size ( void ) OCI-Collection->trim(): Löscht am Ende beginnend Elemente der Collection. Die allgemeine Syntax lautet: bool trim ( int num ) 2.1.2.2 Große Binärobjekte Unter dem allgemeinen Akronym LOB werden drei verschiedene Binärdatentypen in Oracle erfasst, die ebenfalls mit den OCI-Funktionen bearbeitet werden können. Ein solches „large object“ erlaubt die Speicherung von unstrukturierten Daten wie Text, Bilder, Video oder Ton bis zu einer Größe von 4 GB. Die verschiedenen Datentypen sind in Wirklichkeit Zeiger (lob locators), die entweder in einer externen Datei, innerhalb einer Reihe oder außerhalb einer Reihe gespeichert werden. Die Spalten, welche auf einem der LOB-Datentypen basieren, speichern einen solchen Zeiger, während die Daten entweder in der Datenbank (innerhalb oder außerhalb der Reihe) oder im Dateisystem gespeichert werden. Die verschiedenen Programmiersprachen wie PHP oder PL/SQL arbeiten mit diesen Zeigern. Ein BLOB speichert dabei große, unstrukturierte binäre Daten innerhalb der Datenbank innerhalb oder außerhalb einer Zeile. Sie nehmen an Transaktionen teil, sind wiederherstellbar und können kopiert werden. Ein CLOB speichert große Zeichendatenmengen, deren Zeichen eine feste Anzahl Bytes pro Zeichen erfordern, innerhalb der Datenbank innerhalb oder außerhalb 78 OCI-Funktionen einer Zeile. Sie nehmen an Transaktionen teil, sind wiederherstellbar und können kopiert werden. Ein BFILE schließlich wird nicht in der Datenbank gespeichert, sondern stellt eine externe Datei im Dateisystem dar. Die Daten sind nur lesbar und könne nicht geändert, wiederhergestellt oder kopiert werden. Sie nehmen nicht an Transaktionen teil. Das Dateisystem sorgt für die Dateiintegrität. Der Zeiger enthält einen Verzeichnis-Alias, der für einen vollständigen Pfadnamen steht. Die folgenden Funktionen und Methoden unterstützen den PHP-Programmierer bei seiner Arbeit mit LOBs in der Oracle-Datenbank. OCI-Lob->append(): Hängt die Daten von einem LOB an einen anderen an. Dazu muss vorher die Pufferung ausgeschaltet werden. Die allgemeine Syntax lautet: bool append ( OCI-Lob lob_from ) OCI-Lob->close(): Schließt in Zusammenhang mit der writeTemporary()- Funktion eine LOB-Ressource. Die allgemeine Syntax lautet: bool close ( void ) OCI-Lob->eof(): Prüft darauf, ob das Dateiende in einer LOB-Ressource erreicht ist. Die allgemeine Syntax lautet: bool eof ( void ) OCI-Lob->erase(): Löscht einen speziellen Bereich in einem LOB, wobei Beginn und Ende als Parameterwerte übergeben werden. Die allgemeine Syntax lautet: int erase ( [int offset [, int length]] ) OCI-Lob->export(): Speichert den Inhalt eines LOBs in eine Datei. Alias: OCILob->writeToFile(). Die allgemeine Syntax lautet: bool export ( string filename [, int start 79 OCI-Funktionen [, int length]] ) OCI-Lob->flush(): Schreibt Puffer des LOBs zum Server. Durch die Konstante OCI_LOB_BUFFER_FREE wird die Ressource gelöscht. Die allgemeine Syntax lautet: bool flush ( [int flag] ) OCI-Lob->free(): Löscht die mit dem LOB verbundene Ressource. Die allgemeine Syntax lautet: bool free ( void ) OCI-Lob->getBuffering(): Liefert den aktuellen Puffer-Status des LOBs. Die allgemeine Syntax lautet: bool getBuffering ( void ) OCI-Lob->import(): Importiert Textinhalt oder sonstige Daten in den LOB. Alias: OCI-Lob->saveFile(). Die allgemeine Syntax lautet: bool import ( string filename ) OCI-Lob->load(): Liefert den Inhalt des LOBs. Die allgemeine Syntax lautet: string load ( void ) OCI-Lob->read(): Liest einen Teil des LOBs bis zur angegebenen Länge. Die allgemeine Syntax lautet: string read ( int length ) OCI-Lob->rewind(): Setzt den internen Zeiger auf den LOB-Beginn. Die allgemeine Syntax lautet: bool rewind ( void ) OCI-Lob->save(): Speichert Daten im LOB, wobei auch optional angegeben werden kann, ab welcher Stelle die Daten gespeichert werden sollen. Die allgemeine Syntax lautet: bool save ( string data [, int offset] ) OCI-Lob->seek(): Setzt den Zeiger an eine Stelle im LOB. Dabei gelten folgende Konstanten: OCI_SEEK_SET (Position gleich Parameter offset), OCI_SEEK_CUR (aktuelle Position zuzüglich des Wertes von offset-Parameter), OCI_SEEK_END (LOB-Ende zuzüglich (negativen) offset-Parameter). Die allgemeine Syntax lautet: bool seek ( int offset 80 OCI-Funktionen [, int whence] ) OCI-Lob->setBuffering(): Ändert den aktuellen Puffer-Status des LOB. Die allgemeine Syntax lautet: bool setBuffering ( bool on_off ) OCI-Lob->size(): Liefert die Größe des LOB. Die allgemeine Syntax lautet: int size ( void ) OCI-Lob->tell(): Liefert die interne Position des Zeigers im LOB. Die allgemeine Syntax lautet: int tell ( void ) OCI-Lob->truncate(): Beschneidet und löscht den LOB komplett oder um eine optionale Länge. Die allgemeine Syntax lautet: bool truncate ( [int length] ) OCI-Lob->write(): Schreibt Daten in den LOB unter optionalen Angabe einer Länge. Die allgemeine Syntax lautet: int write ( string data [, int length] ) OCI-Lob->writeTemporary(): Schreibt einen temporären LOB unter Angabe von Daten und auch optional eines LOB-Typs. Als Typen können OCI_TEMP_BLOB für einen temporären BLOB oder OCI_TEMP_CLOB für einen temporären CLOB angegeben werden. Die allgemeine Syntax lautet: bool writeTemporary ( string data [, int lob_type] ) oci_lob_copy(): Kopiert den Inhalt eines LOBs in einen anderen LOB. Dabei lässt sich auch eine Länge angeben. Die allgemeine Syntax lautet: bool oci_lob_copy ( OCI-Lob lob_to, OCI-Lob lob_from 81 OCI-Funktionen [, int length] ) oci_lob_is_equal(): Prüft darauf, ob zwei LOB-/FILE-Ressourcen gleich sind. Die allgemeine Syntax lautet: bool oci_lob_is_equal ( OCI-Lob lob1, OCI-Lob lob2 ) oci_new_descriptor(): Erstellt eine neue leere LOB-/FILE-Ressource. Als mögliche Typen können OCI_D_FILE, OCI_D_LOB und OCI_D_ROWID angegeben werden. Die allgemeine Syntax lautet: OCI-Lob oci_new_descriptor ( resource connection [, int type] ) 2.1.2.3 Datenbankverbindung oci_close(): Schließt die Oracle-Verbindung. Die allgemeine Syntax lautet: bool oci_close ( resource connection ) oci_connect(): Richtet eine neue Oracle-Verbindung ein. Der Sitzungsmodus besteht seit Version 1.1 aus den durch sonstige Oracle-Verbindungen bekannten Parametern OCI_DEFAULT, OCI_SYSOPER und OCI_SYSDBA. Seit Oracle 9.2 lässt sich der Zeichensatz, der für eine neue Verbindung genutzt werden soll, über den charset Parameter vorgeben. Von frühreren Versionen wird der Umgebungsparameter NLS_LANG benutzt. Die allgemeine Syntax lautet: resource oci_connect ( string username, string password [, string db [, string charset [, int session_mode]]] ) 82 OCI-Funktionen oci_new_connect(): Während bereis eine Verbindung besteht, richtet diese Funktion eine neue Verbindung ein. Die allgemeine Syntax ist analog zu oci_connect(). oci_pconnect(): Erstellt eine persistente Verbindung. Die allgemeine Syntax ist analog zu oci_pconnect(). oci_password_change(): Wechselt dass Passwort für den angemeldeten Benutzer. Die allgemeine Syntax lautet: bool oci_password_change ( resource connection, string username, string old_password, string new_password ) Seit Version 1.1: resource oci_password_change ( string dbname, string username, string old_password, string new_password ) 2.1.2.4 Transaktionen oci_commit(): Bestätigt eine Reihe von Anführungen bzw. beendet eine Transaktion. Die allgemeine Syntax lautet: bool oci_commit ( resource connection ) oci_rollback(): Setzt eine Reihe von Anweisungen und damit eine Transaktion zurück. Die allgemeine Syntax lautet: bool oci_rollback ( resource connection ) 83 OCI-Funktionen 2.1.2.5 Fehler oci_error(): Liefert den letzten Fehler einer Verbindung. Vorgänger: ocierror() Die allgemeine Syntax lautet: array oci_error ( [resource source] ) oci_internal_debug(): Schaltet die interne Textausgabe für Fehlermeldungen ein (Wert 1) oder aus (Wert 0). Vorgänger: ociinternaldebug(). Die allgemeine Syntax lautet: void oci_internal_debug ( int onoff ) 2.1.2.6 Anweisungen oci_define_by_name(): Bindet eine PHP-Variable an eine Spalte, die in einer SELECT-Anweisung mit Werten gefüllt wird. Vorgänger: ocidefinebyname(). Die allgemeine Syntax lautet: bool oci_define_by_name ( resource statement, string column_name, mixed &variable [, int type] ) oci_bind_by_name(): Bindet eine PHP-Variable an Anweisungen, in denen Platzhalter für die PHP-Variable auftauchen.Vorgänger: ocibindbyname(). Dabei ist neben dem Anweisungshandle, der PHP-Name, der Platzhaltername, die Länge oder -1 als Ersatzwert für die tatsächlich verbrauchte Länge sowie der Datentypname anzugeben. Die allgemeine Syntax lautet: bool oci_bind_by_name ( resource stmt, string ph_name, mixed &variable [, int maxlength [, int type]] ) 84 OCI-Funktionen Folgende Datentypen lassen sich angeben: – OCI_B_FILE für BFILEs; – OCI_B_CFILE für CFILEs – OCI_B_CLOB für CLOBs – OCI_B_BLOB für BLOBs – OCI_B_ROWID für ROWIDs – OCI_B_NTY für benannte Datentypen, d.h. über CREATE TYPE erstellte Daten- typen – OCI_B_CURSOR für Cursor, die mit oci_new_cursor() erstellt worden. oci_cancel(): Beendet das Lesen von einem Cursor. Vorgänger: ocicancel(). Die allgemeine Syntax lautet: bool oci_cancel ( resource stmt ) oci_execute(): Führt eine Anweisung aus. Die allgemeine Syntax lautet: bool oci_execute ( resource stmt [, int mode] ) oci_fetch_all(): Überträgt alle Ergebnisreihen in ein Array. Dabei kann man die Anzahl der zu überspringenden Zeilen, der maximal abzurufenden Zeilen und die Art und Weise, wie das Array erzeugt werden soll, vorgeben. Leere Felder, d.h. NULL-Felder, werden auf den PHP-Wert null gesetzt. Vorgänger: ocifetchistatement(). Die allgemeine Syntax lautet: int oci_fetch_all ( resource statement, array &output [, int skip [, int maxrows [, int flags]]] ) 85 OCI-Funktionen Folgende Konstanten können bei dieser Funktion verwendet werden: – OCI_FETCHSTATEMENT_BY_ROW, um die Arrays pro Zeile zu erstellen. – OCI_FETCHSTATEMENT_BY_COLUMN (Standard), um die Arrays pro Spalte zu erstellen. – OCI_NUM, um ein numerisches Array zu erstellen. – OCI_ASSOC, um ein assoziatives Array zu erstellen. oci_fetch_array(): Ruft die nächste Ergebniszeile in ein assoziatives, numerisches oder beide Schlüsselarten enthaltendes Array ab. Die allgemeine Syntax lautet: array oci_fetch_array ( resource statement [, int mode] ) Als Modi kommen folgende Werte in Betracht: – OCI_BOTH liefert ein Array mit assoziativen und numerischen Schlüsseln zurück und entspricht damit der Angabe OCI_ASSOC + OCI_NUM. Dies ist der Standardwert. – OCI_ASSOC liefert ein assoziatives Array wie oci_fetch_assoc() zurück, dessen Spaltennamen die Arrayschlüssel darstellen. – OCI_NUM liefert ein numerisches Array wie oci_fetch_row() zurück, dessen Arrayschlüssel die 1-basierten Spaltennummern darstellen. – OCI_RETURN_NULLS liefert leere Elemente für NULL-Felder, was für dynamische Ausgabe von bspw. Tabellen sehr wichtig ist, um keine Verschiebungen zu erzielen. – OCI_RETURN_LOBS liefert von einem Deskriptor einen LOB zurück. oci_fetch_assoc(): Liefert die nächste Ergebniszeile in ein assoziatives Array. Die Spaltennamen werden dabei zu Array-Schlüsseln. NULL-Felder werden berücksichtigt.Der Rückgabewert ist false, sobald keine weiteren Reihen 86 OCI-Funktionen gefunden werden. Die allgemeine Syntax lautet: array oci_fetch_assoc ( resource statement ) oci_fetch_object(): Liefert die nächste Ergebniszeile in ein Objekt. Die Spaltennamen werden dabei zu Eigenschaftsnamen. NULL-Felder werden berücksichtigt.Der Rückgabewert ist false, sobald keine weiteren Reihen gefunden werden. Die allgemeine Syntax lautet: object oci_fetch_object ( resource statement ) oci_fetch_row(): Liefert die nächste Ergebniszeile in ein numerisches Array. Die 1-basierte Spaltennummer wird dabei zu einem Array-Schlüssel. NULL-Felder werden berücksichtigt. Der Rückgabewert ist false, sobald keine weiteren Reihen gefunden werden. Die allgemeine Syntax lautet: array oci_fetch_row ( resource statement ) oci_fetch(): Überträgt die nächste Reihe. Vorgänger: ocifetch(). Die allgemeine Syntax lautet: bool oci_fetch ( resource statement ) oci_free_statement(): Löscht die Ressource, die mit einer Anweisung verbunden ist. Vorgänger: ocifreestatement(). Die allgemeine Syntax lautet: bool oci_free_statement ( resource statement ) oci_new_cursor(): Erstellt einen neuen Cursor für Ausführung einer Anweisung. Vorgänger: ocinewcursor(). Die allgemeine Syntax lautet: resource oci_new_cursor ( resource connection ) oci_parse(): Analysiert eine Anweisung, die ausgeführt werden soll. Vorgänger: ociparse(). Die allgemeine Syntax lautet: resource oci_parse ( resource connection, string query ) oci_result(): Liefert einen Feldwert von einer übertragenen Reihe. Als Feldangabe kann man die 1-basierte Spaltennummer oder den Spaltennamen verwenden. Vorgänger: ociresult(). Als Die allgemeine Syntax lautet: mixed oci_result ( resource statement, 87 OCI-Funktionen mixed field ) oci_set_prefetch(): Gibt an, wieviele Reihe vorab abgerufen werden sollen. Vorgänger: ocisetprefetch(). Der Standardwert ist 1. Die allgemeine Syntax lautet: bool oci_set_prefetch ( resource statement [, int rows] ) 2.1.2.7 Metadaten oci_statement_type(): ocistatementtype(). Liefert den Typ Die allgemeine der Anweisung. Vorgänger: Syntax lautet: string oci_statement_type ( resource statement ) Mögliche Werte sind: – SELECT bei einer Abfrage. – UPDATE, DELETE, INSERT bei einer Datenmanipulation (aktualisieren, erfassen, löschen). – CREATE, DROP, ALTER bei einer Bearbeitung von Schema-Objekten (ändern, erfassen, löschen) wie Tabellen, Prozeduren oder Funktionen. – BEGIN, DECLARE bei einem PL/SQL-Block in Form eines anonymen Blocks zur Ausführung von bspw. Prozeduren. – UNKNOWN für sonstige Typen. oci_num_fields(): Liefert die Anzahl der Felder einer Abfrage. Vorgänger: ocinumcols(). Die allgemeine Syntax lautet: int oci_num_fields ( resource statement ) oci_num_rows(): Liefert die Anzahl der betroffenen Reihen einer Anweisung, allerdings nicht von einer Abfrage. Vorgänger: ocirowcount(). Die allgemeine Syntax lautet: int oci_num_rows ( resource stmt ) 88 OCI-Funktionen oci_field_is_null(): Prüft darauf, ob ein Feld den Wert NULL enthält. Vorgänger: ocicolumnisnull(). Die allgemeine Syntax lautet: bool oci_field_is_null ( resource stmt, mixed field ) oci_field_name(): Liefert den Feldnamen. Vorgänger: ocicolumnname(). Die allgemeine Syntax lautet: string oci_field_name ( resource statement, int field ) oci_field_precision(): Liefert die Anzahl Vorkommastellen eines Feldes. Vorgänger: ocicolumnprecision(). Die allgemeine Syntax lautet: int oci_field_precision ( resource statement, int field ) oci_field_scale(): Liefert die Anzahl der Nachkommastellen eines Feldes. Vorgänger: ocicolumnscale(). Die allgemeine Syntax lautet: int oci_field_scale ( resource statement, int field ) oci_field_size(): Liefert die Größe eines Feldes. Vorgänger: ocicolumnsize(). Die allgemeine Syntax lautet: int oci_field_size ( resource stmt, mixed field ) oci_field_type_raw(): Liefert den RAW-Datentyp eines Feldes. Vorgänger: ocicolumntyperaw(). Die allgemeine Syntax lautet: int oci_field_type_raw ( resource statement, int field ) oci_field_type(): Liefert den Datentyp eines Feldes. Vorgänger: ocicolumntype(). Die allgemeine Syntax lautet: mixed oci_field_type ( resource stmt, int field ) oci_server_version(): ociserverversion(). Liefert die Die allgemeine Server-Version. Vorgänger: Syntax lautet: string oci_server_version ( resource connection ) 2.1.3 Konstanten Die Konstanten sind für verschiedene Funktionen insbesondere als Übergabeparameter wichtig und werden hier in verschiedene Gruppe eingeteilt. 89 OCI-Funktionen Die nachfolgenden Konstanten beeinflussen das Verhalten, wie eine Anweisung allgemein ausgeführt werden soll. Konstante Bedeutung OCI_DEFAULT (int) Die Anweisung bestätigt. OCI_DESCRIBE_ONLY (int) Die Anweisung wird nicht ausgeführt und liefert stattdessen eine Beschreibung der SELECT-Liste. OCI_COMMIT_ON_SUCCESS (int) Automatische Ausführung. wird nicht Bestätigung automatisch nach der Ausführungsmodus Die nachfolgenden Konstanten beeinflussen das Verhalten, wie Abfrageergebnisse abgerufen werden sollen. Konstante Bedeutung OCI_EXACT_FETCH (int) Angabe der maximal abzurufenden Zeilen und des automatischen Schließens des Cursos. OCI FETCHSTATEMENT BY COLUMN Standardmodus von oci_fetch_all(). Das Array wird anhand der Spalten erzeugt (seltene Verwendung). (int) OCI_FETCHSTATEMENT_BY_ROW (int) OCI_ASSOC (int) 90 Alternative Angabe bei oci_fetch_all(). Pro Reihe entsteht ein neues Array mit den einzelnen Spalten als Array-Einträgen (typische Verwendung). Wird verwendet mit oci_fetch_all() und oci_fetch_array() zur Erstellung eines assoziativen Arrays mit den Spaltennamen als Schlüssel. OCI-Funktionen OCI_NUM (int) Wird verwendet mit oci_fetch_all() und oci_fetch_array() zur Erzeugung eines numerischen Arrays mit den 1-basierten Spaltennummern als Schlüssel.. OCI_BOTH (int) Wird verwendet mit oci_fetch_all() und oci_fetch_array() um sowohl die assoziative als auch numerische Variante zu erhalten. OCI_RETURN_NULLS (int) Wird verwendet mit oci_fetch_array(), um leere Array-Elemente für NULL-Felder zu erhalten (typische Verwendung). OCI_RETURN_LOBS (int) Wird verwendet mit oci_fetch_array(), um den Wert eines LOBs zu erhalten (einfachste Verwendung). Abrufmodus Die nachfolgenden Konstanten erleichtern die Angabe von Datentypen. Konstante Bedeutung OCI_SYSDATE (int) Aktuelle Zeit. OCI_B_BFILE oder Wird verwendet mit oci_bind_by_name() bei der Bindung von BFILEs. oder Wird verwendet mit oci_bind_by_name() bei der Bindung von CFILEs. (int) oder Wird verwendet mit oci_bind_by_name() bei der Bindung von CLOBs. (int) oder Wird verwendet mit oci_bind_by_name() bei der Bindung von BLOBs. oder Wird verwendet mit oci_bind_by_name() (int) SQLT_BFILEE (int) OCI_B_CFILEE (int) SQLT_CFILEE (int) OCI_B_CLOB SQLT_CLOB (int) OCI_B_BLOB SQLT_BLOB (int) OCI_B_ROWID (int) 91 OCI-Funktionen SQLT_RDD (int) bei der Bindung von ROWIDs. OCI_B_CURSOR (int) Wird verwendet mit oci_bind_by_name() bei der Bindung von Cursorn, die über oci_new_descriptor() erstellt wurden. OCI_B_NTY (int) oder SQLT NTY Wird verwendet mit oci_bind_by_name() bei der Bindung von benannten Datentypen. (int) PHP < 5.0: OCI_B_SQLT_NTY OCI_B_BIN (int) OCI-Konstanten Die nachfolgenden Konstanten beeinflussen die Erstellung und Verwendung von LOBs. Konstante Bedeutung OCI_LOB_BUFFER_FREE (int) Wird mit OCI-Lob->flush() verwendet, um Puffer zu leeren. OCI_TEMP_CLOB (int) Wird mit OCI-Lob->writeTemporary() verwendet, um explizit anzugeben, dass ein temporärer CLOB erstellt werden soll. OCI_TEMP_BLOB (int) Wird mit OCI-Lob->writeTemporary() verwendet, um explizit anzugeben, dass ein temporärer BLOB erstellt werden soll. (int) oder Weist oci_new_descriptor() an, eine neue FILE-Ressource zu erstellen. (int) oder Weist oci_new_descriptor() an, eine neue LOB-Ressource zu erstellen. (int) oder Weist oci_new_descriptor() an, eine neue ROWID-Ressource zu erstellen.. OCI_DTYPE_FILE OCI_D_FILE (int) OCI_DTYPE_LOB OCI_D_LOB (int) OCI_DTYPE_ROWID OCI_D_ROWID (int) LOB-Modi 92 OCI-Funktionen Die nachfolgenden Konstanten erlauben die Anmeldung mit privilegierter Berechtigung. oci8.privileged_connect sollte in php.ini eingeschaltet sein Konstante Bedeutung OCI_SYSOPER (int) Wird verwendet Verbindung als Berechtigungen OCI_SYSDBA (int) mit zur weiteren oci_connect() SYSOPER mit Wird verwendet mit oci_connect() zur SYSDBA mit weiteren Verbindung als Berechtigungen. Verbindungs-Modi 2.2 Beispiele Dieser Abschnitt zeigt nun, wie die vielen Funktionen im Zusammenhang für die typischen Aufgaben DB-Verbindung, Abfragebearbeitung, Datenänderung, Prozeduraufruf usw. verwendet werden. 2.2.1 DB-Verbindung Ohne Datenbankverbindung lässt sich natürlich gar keine von den schönen Funktionen, die gerade in der Referenz vorgestellt worden sind, verwenden. Damit die Beispiele für verschiedene Datenbanken nutzbar sind, gibt es im Ordner für die OCI-Funktionen eine Datei mit Vorgabewerten für Benutzer, Passwort und Datenbank, welche in die verschiedenen anderen Beispiele eingebunden wird. $user = "SCOTT"; $password = "TIGER"; $database = "KurseNRW"; db_properties.inc.php: Voreinstellungen Das nachfolgende Beispiel erstellt über die verschiedenen für PHP 4 und natürlich auch PHP 5 vorhandenen Funktionen Datenbankverbindungen und löst sie wieder. Das Ver- 93 OCI-Funktionen bindungshandle wird von den beiden Funktionen oci_connect() und ocilogon() zurückgegeben und kann dann zum Lösen der Verbindung über oci_close() und ocilogoff() als Übergabeparameter sowie zum Abrufen von Fehlern verwendet werden. require_once("db_properties.inc.php"); $con; $errors; $html = ""; // oci_-Funktionen if ($con = oci_connect($user, $password, $database)){ $html .= "<p>Verbindung eingerichtet..."; if (oci_close($con)){ $html .= "..und geschlossen.<p>"; } } else { $html .= "<p>Verbindung fehlgeschlagen.</p>"; } // oci-Funktionen if ($con = ocilogon($user, $password, $database)){ $html .= "<p>Verbindung eingerichtet..."; if (ocilogoff($con)){ $html .= "..und geschlossen.<p>"; } } 94 OCI-Funktionen else { $html .= "<p>Verbindung fehlgeschlagen.</p>"; } // ocin-Funktion if ($con = ocinlogon($user, $password, $database)){ $html .= "<p>Verbindung eingerichtet..."; if (ocilogoff($con)){ $html .= "..und geschlossen.<p>"; } } else { $html .= "<p>Verbindung fehlgeschlagen.</p>"; } echo $html; oci_verbindung.php: Datenbankverbindung auf- und abbauen Im Browser sollten dann die drei Ausgaben erscheinen, welche bestätigen, dass die DBVerbindung eingerichtet und dann gelöscht wurde. 95 OCI-Funktionen Abbildung 2.1: Ausgabe im Browser Bei einer Fehlermeldung wie bspw. einem falsch geschriebenen Passwort erhält man eine PHP-Warnung, welche hauptsächlich aus dem Oracle-Fehlertext besteht. Eine solche Meldung kann über die Fehlerfunktion nicht ermittelt werden, weil diese stets ein gültiges Verbindungshandle als Übergabeparameter erwartet. Abbildung 2.2: Ausgabe im Browser bei Fehler 2.2.2 Abfragen ausführen Sobald die Verbindung besteht, stellt sich die Frage, auf welche Weise man Abfragen ausführen kann, um Daten aus der Datenbank in die HTML-Seite zu zaubern. Dazu 96