PHP und Oracle - Comelio Medien

Werbung
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 = " ";
// 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
Herunterladen