- Datenbanken erzeugen
- Tabellen, Datentypen, Constraints
- Der Verbindungsaufbau mit PHP
- Mit dem Server kommunizieren
- Daten eingeben
- Eine Bibliotheksdatei anlegen
- Ein Eingabeformular
- Den Inhalt der Tabelle fahrt auflisten
|
| PostgreSQL ist ein objektrelationales Open-Source Datenbankmanagementsystem, das neben dem SQL92-Standard viele eigene Erweiterungen sowie Implementierungen des SQL99-Standards anbietet. Es ist für große Datenbestände bestens geeignet, da es dem Benutzer fast keine Einschränkungen auferlegt. So können Tabellen bis zu 64 TerraByte groß sein und maximal 1600 Spalten enthalten und die Größe einer Datenbank ist unbegrenzt.
|
| Die Entwickler von PostgreSQL, 'The PostgreSQL Global Development Group', stellen sich unter der Adresse http://developer.postgresql.org/index.php vor. |
| PostgreSQL ist in allen Linux-Distributionen enthalten, aber wahrscheinlich nicht in einer aktuellen Version. Diese ist auf der Seite http://www.postgresql.org zu haben. Dort finden Sie immer Verweise zu den aktuellsten Versionen zum Download, ob als Sourcepakete oder als RPMs. Auf der Seite http://www.postgresql.org/docs/index.html sind die Dokumentationen, leider nur in Englisch, in verschiedenen Formaten zum Download angeboten. Eine interaktive Dokumentation, die Sie nach Stichworten durchsuchen können, finden Sie unter http://www.postgresql.org/idocs/. |
| 1. Datenbanken erzeugen |
Im folgenden gehe ich davon aus, dass PostgreSQL installiert ist und das System bereits einen Superuser postgres angelegt hat. Dieser wird der Besitzer der Datenbanken. Es ist ratsam, mit PostgreSQL unter einem anderen Benutzer zu arbeiten. Dazu legen Sie als Benutzer postgres mittels der Client Application createuser (siehe Reference Manual, Client Applications) einen neuen Benutzer dasbinich an: |
|
| Beantworten Sie die Fragen, ob der neue Benutzer Datenbanken und Benutzer anlegen darf, mit ja. |
Zum Erzeugen von Datenbanken ist der Aufruf der Client Application createdb auf der Kommandozeile die einfachste Methode: |
$ createdb
CREATE DATABASE | |
Damit haben Sie eine Datenbank mit ihrem Benutzernamen auf dem lokalen Rechner erzeugt, die über den Standardport 5432 mit dem Datenbankserver kommuniziert und keine Passworteingabe verlangt. Dass die Datenbank von nun an existiert, bestätigt PostgreSQL mit CREATE DATABASE. Selbstverständlich kann man der Applikation createdb mehrere Optionen mitgeben, die gebräuchlichsten sind: |
$ createdb mz
CREATE DATABASE | |
| erzeugt die Datenbank mz mit den Standardeinstellungen. Datenbankobjekte gehören in PostgreSQL dem Benutzer, der sie erzeugt. Diese Datenbank wird mit dem folgenden Kommando im Datenbankmonitor geöffnet: |
$ psql mz
Welcome to psql, the PostgreSQL interactive terminal.
Type: \copyright for distribution terms
\h for help with SQL commands
\? for help on internal slash commands
\g or terminate with semicolon to execute a query
\q to quit
mz=# | |
Mit mz=# signalisiert der Datenbankserver seine Betriebsbereitschaft. In diesem Prompt wird immer der Name der gerade geöffneten Datenbank angezeigt. Innerhalb von psql gibt es eine ganze Menge Kommandos, die alle mit einem Backslash beginnen. Mit der Eingabe \? kömmen Sie alle diese Metakommandos anzeigen. Um alle existierenden Datenbanken aufzulisten, benutzen Sie \l (list). |
mz=# \l
List of databases
Database | Owner | Encoding
-----------|-------|----------
mz | conni | SQL_ASCII
template1 | conni | SQL_ASCII
(2 rows) | |
| 2. Tabellen, Datentypen, Constraints |
| Neue Tabellen werden mit dem SQL-Befehl CREATE TABLE definiert. Die Syntax dieses Kommandos zeigt bereits viele Leistungsmerkmale von PostgreSQL: Es unterstützt temporäre Tabellen, Vererbung sowie den FOREIGN KEY-Constraint zur Prüfung und Aufrechterhaltung der referentiellen Integrität. Erklärungen zu den einzelnen Schlüsselworten finden Sie im Reference Manual, einem Teil der Dokumentation. |
CREATE [ { TEMPORARY | TEMP } ] TABLE tabellen_name (
{ feldname datentyp [ DEFAULT vorgabewert ]
[ spalten_constraint [, ... ] ]
| tabellen_constraint } [, ... ]
) [ INHERITS ( elterntabelle [, ... ] ) ]
[ WITH OIDS | WITHOUT OIDS ] | |
| mit spalten_constraints als: |
[ CONSTRAINT constraint_name ]
{ NOT NULL | NULL | UNIQUE | PRIMARY KEY |
CHECK (ausdruck) |
REFERENCES ref_tabelle [ ( ref_spalte ) ] [ MATCH FULL ]
[ ON DELETE aktion ] [ ON UPDATE aktion ] }
[ DEFERRABLE | NOT DEFERRABLE ]
[ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] | |
| und tabellen_constraints als: |
[ CONSTRAINT constraint_name ]
{ UNIQUE ( feldname [, ... ] ) |
PRIMARY KEY ( feldname [, ... ] ) |
CHECK ( ausdruck ) |
FOREIGN KEY ( feldname [, ... ] )
REFERENCES ref_tabelle [ ( ref_spalte [, ... ] ) ]
[ MATCH FULL ] [ ON DELETE aktion ] [ ON UPDATE aktion ] }
[ DEFERRABLE | NOT DEFERRABLE ]
[ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] | |
| Mit den Fähigkeiten von PostgreSQL können Sie bereits bei der Datendefinition auf der Ebene des Datenbanksystems, und damit unabhängig von jeder Applikation, viele Prüfroutinen vorwegnehmen, die sonst von einer Anwendung implementiert werden müssten. Diese Routinen auf Datenbankebene werden Constraints genannt. So können CHECK-Constraints die Eingabedaten über die normale Typprüfung hinaus überwachen, FOREIGN KEY-Constraints kontrollieren die Zulässigkeit von Datenmanipulationen oder legen das Verhalten des Systems bei UPDATE- oder DELETE-Ereignissen fest. |
| PostgreSQL stellt Ihnen eine große Auswahl von Datentypen zur Verfügung, die den SQL-Standard einschliesst und um eigene Typen erweitert. Nicht standardkonform ist beispielsweise der Typ TEXT, der Zeichenketten unbegrenzter Länge aufnehmen kann, oder der Typ OID, der ein eindeutiger Object-Identifier ist. Eine Beschreibung der Datentypen finden Sie im Kapitel 3 des User's Guide. Als objektrelationales Datenbanksystem bietet PostgreSQL auch die Möglichkeit, selber Datentypen zu erzeugen. |
Als Beispiel soll eine sehr einfache Mitfahrzentrale entworfen werden. In einer Mitfahrzentrale gibt es Fahrten, die von Fahrern angeboten werden. Dazu braucht man eine Tabelle fahrer und eine Tabelle fahrt. |
mz=# CREATE TABLE fahrer(
mz-# id SERIAL PRIMARY KEY,
mz-# email VARCHAR(50) NOT NULL UNIQUE,
mz-# pwd CHAR(15) NOT NULL UNIQUE
mz-# CHECK (length(btrim(pwd)) > 5));
NOTICE: CREATE TABLE will create implicit
sequence 'fahrer_id_seq' for SERIAL column 'fahrer.id'
NOTICE: CREATE TABLE/PRIMARY KEY will create implicit
index 'fahrer_pkey' for table 'fahrer'
NOTICE: CREATE TABLE/UNIQUE will create implicit
index 'fahrer_email_key' for table 'fahrer'
NOTICE: CREATE TABLE/UNIQUE will create implicit
index 'fahrer_pwd_key' for table 'fahrer'
CREATE | |
Diese Tabelle hat das Attribut id als Primärschlüssel. In PostgreSQL sind Primärschlüssel standardmäßig UNIQUE und NOT NULL, so dass diese Modifizierer nicht explizit angegeben werden müssen. Sie sehen in den NOTICE-Meldungen, die von PostgreSQL zurückgegeben werden, dass automatisch ein Index für jeden Primärschlüssel angelegt wird.
|
Jede Definition eines UNIQUE-Feldes erzeugt ebenfalls einen impliziten Index. Die angelegten Indizes lassen Sie sich mit \di anzeigen: |
mz=# \di
List of relations
Name | Type | Owner
------------------+-------+---------
fahrer_email_key | index | conni
fahrer_pwd_key | index | conni
fahrer_pkey | index | conni
(3 rows) | |
Der Datentyp SERIAL initialisiert einen internen Zähler, der bei jeder INSERT-Operation um 1 hochgezählt wird und so für eindeutige IDs sorgt. Intern wird dadurch implizit eine Sequenz als Datenbankobjekt mit dem Namen tabellenname_feldname_seq, hier also fahrer_id_seq, erzeugt (siehe NOTICE), auf die man mit speziellen Funktionen zugreifen kann. Beispielsweise können Sie mit dem Aufruf von |
mz=# SELECT currval('fahrer_id_seq'); | |
| den aktuellen Zählerstand abfragen. Beim Einfügen eines Datensatzes wird dieses Feld nicht in der Feldliste aufgeführt, da PostgreSQL den Wert automatisch vergibt. |
Dass PostgreSQL die Sequenz erzeugt hat, können Sie mit dem Kommando \d (display) anzeigen lassen, das alle Datenbankobjekte auflistet. |
mz=# \d
List of relations
Name | Type | Owner
---------------+----------+---------
fahrer | table | conni
fahrer_id_seq | sequence | conni
(2 rows) | |
Bei der Definition des Feldes pwd ist ein sogenannter CHECK-Constraint definiert. CHECK-Constraints prüfen die Werte, bevor Sie in die Tabelle eingefügt werden und lehnen Eingaben ab, die der CHECK-Bedingung nicht genügen. Die hier definierte Bedingung benutzt zwei hintereinandergeschaltete PostgreSQL-Funktionen, die die Länge length() des eingegebenen Werts ohne führende oder abschliessende Leerzeichen btrim() berechnen. Diese Länge muss > 5 sein, damit die Eingabe akzeptiert wird, ansonsten wird kein INSERT durchgeführt. CHECK-Constraints sind nützliche Instrumente zur Sicherung der Datenkonsistenz. |
| Eine Mitfahrtzentrale braucht auch eine Tabelle zur Speicherung der angebotenen Fahrten: |
mz=# CREATE TABLE fahrt(
mz-# id SERIAL PRIMARY KEY,
mz-# start VARCHAR(20) NOT NULL CHECK (length(trim(start)) > 2),
mz-# ziel VARCHAR(20) NOT NULL CHECK (length(trim(ziel)) > 2),
mz-# plaetze SMALLINT DEFAULT 1 NOT NULL,
mz-# datum DATE NOT NULL,
mz-# raucher BOOLEAN,
mz-# fahrer INTEGER NOT NULL,
mz-# FOREIGN KEY (fahrer) REFERENCES fahrer (id));
CREATE | |
In dieser Definition ist mit dem Feld fahrer ein Fremdschlüssel enthalten, der das Feld id der Tabelle fahrer referenziert. Dieser Constraint ist als Tabellenconstraint definiert, er könnte genausogut als Spaltenconstraint definiert sein. Dann hiesse die entsprechende Zeile : |
fahrer INTEGER NOT NULL REFERENCES fahrer (id) | |
Jedes Feld, das mit einer Referenz auf ein Feld einer anderen Tabelle definiert ist, wird als Fremdschlüssel betrachtet. Ist der Name des referenzierten Feldes nicht angegeben, nimmt PostgreSQL automatisch den Primärschlüssel der referenzierten Tabelle an. Das heisst, die Angabe von id ist in unserem Fall nicht notwendig, erhöht aber die Lesbarkeit. |
Mit der Definition des FOREIGN KEYS ist es nun nicht mehr möglich, Datensätze aus der Tabelle fahrer zu löschen, falls Einträge in der Tabelle fahrt existieren, die diesen Fahrer als Fremdschlüssel enthalten. Genauso verhindert der Constraint Einträge in der Tabelle fahrt, die eine Fahrer-ID enthalten, die in der Tabelle fahrer nicht existiert. Mit FOREIGN KEYS können Beziehungen zwischen Tabellen auf die Datenbankstruktur abgebildet werden. Machen wir die Probe aufs Exempel: |
mz=# INSERT INTO fahrt (
mz-# start, ziel, plaetze, datum, raucher, fahrer)
mz-# VALUES ('Ulm', 'Kiel', 3, '05.06.2002', TRUE, 12);
ERROR: <unnamed> referential integrity violation
- key referenced from fahrt not found in fahrer | |
| <unnamed> bedeutet, dass dem Constraint bei der Definition kein Name zugewiesen wurde, was bei größeren Projekten aber absolut empfehlenswert ist, da die Fehlersuche dadurch erleichtert wird. |
Bei der Definition des REFERENCES/FOREIGN KEY-Constraints hat PostgreSQL einen Trigger namens fk_fahrer_id generiert, der die Eingaben in die Tabelle kontrolliert. Dieser Trigger verhindert nun den INSERT, da kein Eintrag in der Tabelle fahrer mit der id 12 existiert. FOREIGN KEY-Constraints erzwingen die Reihenfolge der Dateneingaben. Dies entspricht der realen Welt, denn wo kein Fahrer ist, kann auch keine Mitfahrgelegenheit bestehen. |
| Sie können alle Constraint-Triggers, Sequenzen oder impliziten Indizes einsehen, wenn Sie auf der Kommandozeile einen dump der Datenbank erzeugen: |
|
| Dieser Befehl generiert einen Dump der Datenbank mz und speichert diesen als Textdatei in /home/benutzername/mz.out. Hier finden Sie Zeilen wie diese: |
CREATE CONSTRAINT TRIGGER "<unnamed>"
AFTER INSERT OR UPDATE ON "fahrt" FROM "fahrer" NOT
DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE
PROCEDURE "RI_FKey_check_ins" ('<unnamed>',
'fahrt', 'fahrer', 'UNSPECIFIED', 'fahrer', 'id'); | |
| 3. Der Verbindungsaufbau mit PHP |
| Bevor mit einem Skript auf eine Datenbank zugegriffen werden kann, muss eine Verbindung zum Datenbankserver aufgebaut werden. Dies wird von der Funktion resource pg_connect (string connection_string) geleistet, die als Parameter einen String entgegennimmt, der alle Verbindungsoptionen als Name-Wert-Paare enthält und bei Erfolg einen positiven Integerwert als Verbindungskennung (resource) zurückgibt, ansonsten wird FALSE zurückgegeben. |
<?php
/*
$conn = pg_connect ("host=hostname port=portnummer
tty=TTY options=optionen dbname=nameDerDB
user=benutzername password=passwort ");
*/
$conn = pg_connect ("dbname=mz");
?> | |
| erzeugt die Verbindung zu der Datenbank mz. Als Host wird localhost angenommen, die Verbindung benutzt den PostgreSQL Standardport 5432 und wird unter dem Namen des aktuell angemeldeten Benutzers geöffnet. In der Variablen $conn wird die Verbindungskennung zurückgegeben. Wenn das PHP-Skript beendet ist, wird die Verbindung automatisch geschlossen. Sie können jede Verbindung, die mit pg_connect() geöffnet wurde auch explizit mit der Funktion bool pg_close (resource connection) schließen. |
Wenn Sie keinen Benutzernamen im Verbindungsstring angeben, müssen Sie beachten, dass bei Webanwendungen der Webserver der Benutzer der Datenbank ist. Sie müssen dann mit createuser NameDesWebservers einen Benutzer für den Webserver einrichten und ihm die entsprechenden Zugriffsrechte auf die Datenbanktabellen erteilen. Anderenfalls reagiert PostgreSQL mit einer Permission denied-Fehlermeldung. Der Besitzer der Datenbankobjekte hat automatisch alle Zugriffsrechte auf seine Datenbankobjekte. Anderen Benutzern werden mit GRANT Privilegien erteilt und mit REVOKE wieder entzogen. Um dem Webserver für die Tabelle fahrt SELECT-, UPDATE- und DELETE-Rechte sowie das Recht, Regeln zu definieren, zu übertragen, wird das folgende Statement an den Server gesendet. |
mz=# GRANT ALL ON fahrt TO apache; | |
Der Webserver hat in den verschiedenen Linux-Distributionen unterschiedliche Namen, bei Red Hat heißt er ab 7.1 apache, bei Suse wwwrun und in anderen Distributionen nobody. Alle Zugriffsrechte auf der aktuellen Datenbank können Sie mit \z anzeigen. |
| 4. Mit dem Server kommunizieren |
| Nachdem die Verbindung aufgebaut wurde, können Sie nun mit der PHP-Funktion resource pg_exec (resource connection, string query) bzw. resource pg_query (resource connection, string query) einen Abfragestring an den Datenbankserver senden und erhalten eine Verbindungskennung zurück. Viele PostreSQL-Funktionen haben ab der PHP-Version 4.2 neue Funktionsnamen mit zusätzlichen Unterstrichen erhalten und einige neue Funktionen wurden zugefügt. Beispielsweise heißt die Funktion pg_exec() ab PHP 4.2.0 pg_query(). Die alten Funktionsnamen können aber noch weiterverwendet werden. |
| pg_query() nimmt zwei Argumente entgegen, einmal die Verbindungskennung, die von pg_connect() zurückgegeben wurde, und einen Abfragestring, der an den Datenbankserver gesendet wird. Auch wenn noch keine Tabellen erzeugt sind, können Sie bereits Abfragen an den Server senden, beispielsweise, um Umgebungsvariablen abzufragen. |
mz=# select current_user;
current_user
--------------
conni
(1row) | |
| Da bereits eine Tabelle existiert, kann man folgendes Skript ausführen, das statistische Informationen zurückgibt. Beachten Sie, dass hier die neuen Funktionsnamen von PHP 4.2.0 verwendet wurden. |
<html>
<head><title>Demo</title></head>
<body>
<?php
$host = "localhost";
$port = "5432";
$user = "conni";
$password = "";
$dbname = "mz";
// eine Verbindung zum Datenbankserver aufbauen
$conn = @pg_connect ("dbname=$dbname user=$user
host=$host port=$port")
or die ("Konnte keine Verbindung herstellen.");
$db = pg_dbname ($conn);
echo 'Name der DB: '.$db.'<br>';
$res = pg_query ("select * from fahrt");
$nf = pg_num_fields ($res);
echo 'Anzahl der Felder: '.$nf.'<br>';
?>
<table border="1">
<tr align="left"><th>Feld</th><th>Name</th>
<th>Typ</th><th>Groesse</th></tr>
<?php
for ($i = 0; $i < $nf; $i++) {
echo '<tr><td>'.$i.'</td>';
echo '<td>'.pg_field_name ($res, $i).'</td>';
echo '<td>'.pg_field_type ($res, $i).'</td>';
echo '<td>'.pg_field_size ($res, $i).'</td>';
}
echo '</table>';
?>
</body></html> | |
| Die Ausgabe im Browser: |
Name der DB: mz
Anzahl der Felder: 7
Feld Name Typ Groesse
0 id int4 4
1 start varchar -1
2 ziel varchar -1
3 plaetze int2 2
4 datum date 4
5 raucher bool 1
6 fahrer int4 4 | |
| Die Größe von -1 bei dem Typ VARCHAR kennzeichnet ein Feld mit variabler Länge. |
| 5. Daten eingeben |
| Wenn Sie im Datenbankmonitor arbeiten, wird zur Eingabe der Daten ein INSERT-Kommando an den Datenbankserver gesendet: |
mz=# INSERT INTO fahrer (email, pwd)
mz=# VALUES ('me@home.de', 'meathome');
INSERT 123456 1 | |
| PostgreSQL bestätigt ein erfolgreiches Einfügekommando mit der Rückgabe des OID und der Anzahl der eingefügten Datensätze. Der OID ist ein systemweit eindeutiger Object-Identifier. Jeder Datensatz und alle Datenbankobjekte haben, wie für objektrelationale Datenbanksysteme gefordert, in PostgreSQL eindeutige Bezeichner. Alle Abfragen, die direkt im Datenbankmonitor an den Server gesendet werden, können in PHP mit der Funktion pg_query() an den Server gesendet werden. Um noch einen Datensatz in die Tabelle fahrer aus einem Skript heraus einzufügen, wird das INSERT-Kommando an diese Funktion übergeben: |
<?php
$res = pg_query ("INSERT INTO fahrer (email, pwd)
VALUES ('ping@pong.net', 'pingpong')",$conn);
$oid = pg_last_oid ($res);
echo $oid;
?>
| |
| Die Funktion int pg_last_oid (resource result) gibt den Object-Identifier des soeben eingefügten Datensatzes zurück. Sie können sich alle OIDs einer Tabelle anzeigen lassen, indem Sie das Feld oid bei einem SELECT-Kommando mit angeben: |
mz=# SELECT oid, * FROM fahrer;
oid | id | email | pwd
--------+----+---------------+----------
123456 | 1 | me@home.de | meathome
123490 | 2 | ping@pong.net | pingpong
(2 rows) | |
| Wenn Sie größere Datenmengen einfügen möchten, können Sie eine Textdatei mit INSERT-Kommandos erstellen, die Sie im Datenbankmonitor einlesen: |
mz=# \i /pfad/zu/insertdatei | |
| Eine sehr schnelle Lösung zum Importieren oder Exportieren großer Datenmengen bietet der PostgreSQL-Befehl COPY, der auch dazu geeignet ist, Daten aus anderen Anwendungen zu übernehmen bzw. Daten für andere Anwendungen zur Verfügung zu stellen. Sie finden ausführliche Informationen zu COPY im Reference Manual der Dokumentation. |
| 6. Eine Bibliotheksdatei anlegen |
| Immer, wenn ein Skript auf eine Datenbank zugreift, muss zunächst eine Verbindung geöffnet werden. Aus diesem Grund kann man die Erzeugung der Verbindung und weitere Einstellungen, die für alle Skripte gelten, in eine Bibliotheksdatei auslagern, die dann am Anfang des Skripts mit include() eingebunden wird. |
| Zu den Funktionen, die mehrfach in dieser Anwendung gebraucht werden, gehören Prüfroutinen für Passwörter oder Emailadressen.
Diese Bibliotheksdatei soll mz_lib.php heissen. |
<?php /* mz_lib.php */
// der Seitenanfang fuer alle HTML-Dokumente
function oben($title){
return "<html>
<head>
<title>$title</title>
</head>
<body>";
}
// Aufrufparameter fuer Datenbankverbindung setzen
$host = "localhost";
$port = "5432";
$user = "conni";
$password = "";
$dbname = "mz";
// eine Verbindung zum Datenbankserver aufbauen
$conn = @pg_connect ("dbname=$dbname user=$user
port=$port host=$host");
// die Verbindung prüfen und gegebenenfalls eine Fehlermeldung
// an das aufrufende Programm zurückgeben
if (!$conn) {
// Verbindung fehlgeschlagen
$msg = ' Es konnte keine Verbindung hergestellt werden.';
}
else {
// Verbindung hergestellt
// Deutsches Datumsformat setzen
pg_query ($conn, "set datestyle to german");
}
function kdcheck($tab, $nr, $pw) {
// testen, ob es diesen Kunden gibt
// gibt die Anzahl der Datensätze zurück
if ($nr && $pw) {
$sql = "select count(*) from $tab
where id = $nr and pwd = '$pw';";
$res = @pg_query ($sql)
or die ("Konnte die Abfrage nicht ausführen.");
$ok = pg_fetch_result ($res, 0, 0);
return $ok;
}
}
function pwdcheck($tab, $pw) {
// ein neuer Kunde
// testen, ob es dieses Passwort schon gibt
$sql = "select 1 from $tab where pwd = '$pw';";
$res = @pg_query ($sql)
or die ("Konnte die Abfrage nicht ausführen.");
$ok = pg_num_rows ($res);
return $ok;
}
function mailcheck($tab, $mail) {
// ein neuer Kunde
// testen, ob es diese Emailadresse schon gibt
$sql = "select 1 from $tab where email = '$mail';";
$res = @pg_query ($sql)
or die ("Konnte die Abfrage nicht ausführen.");
$ok = pg_num_rows ($res);
return $ok;
}
function neueintrag($tab, $mail, $pw) {
$sqi = "insert into $tab (email, pwd) values ";
$sqi .= "('".pg_escape_string ($mail)."',";
$sqi .= "'".pg_escape_string ($pw)."');";
$res = @pg_query ($sqi)
or die ("Konnte die Abfrage nicht ausführen.");
$resnr = pg_query ("select currval('".$tab."_id_seq');");
$nr = pg_fetch_result ($resnr, 0, 0);
return $nr;
}
// die Fusszeile fuer alle HTML-Dokumente
function unten() {
return '</body></html>';
}
?> | |
| Mit pg_query ($conn, "set datestyle to german") wird das deutsche Datumsformat gesetzt. In PostgreSQL können Umgebungsvariablen mit dem Kommando SET gesetzt werden, die dann für die Dauer der Datenbanksitzung gelten. Außer dem Kommando SET TIME ZONE {'nummerDerZeitzone'}, das auch im SQL92-Standard enthalten ist, sind die SET-Kommandos Erweiterungen von PostgreSQL. Die Angabe der Verbindungskennung $conn ist optional. Wenn sie fehlt, wird die zuletzt geöffnete Verbindung benutzt. |
| Die Funktion int pg_num_rows (resource result) gibt die Anzahl der Zeilen in der Ergebnismenge $res zurück. |
$sql = "select 1 from $tab where pwd = '$pw';" | |
| Diese Abfrage zählt die Datensätze einer Tabelle und gibt für jeden gefundenen Datensatz eine 1 zurück. Man kann diese Syntax benutzen, wenn man nicht an irgendwelchen Feldwerten interessiert ist, sondern nur an der Anzahl der Datensätze. Sinnvoll ist dies nur dann, wenn sehr wenige Datensätze als Ergebnis erwartet werden. Alternativ wird die Aggregatfunktion count(*) verwendet, die die Anzahl der zutreffenden Datensätze liefert. count() gibt immer einen einzigen Datensatz zurück. |
$sqi = "insert into $tab (email, pwd) values ";
$sqi .= "('".pg_escape_string ($mail)."',";
$sqi .= "'".pg_escape_string ($pw)."');"; | |
| Die PHP-Funktion string pg_escape_string (string data) steht seit PHP 4.2.0 zur Verfügung und sorgt dafür, dass Sonderzeichen in Strings, wie Hochkommas oder Umlaute, mit einem Backslash gequoted werden, bevor Sie in die Tabelle geschrieben werden. Ein Hochkomma etwa würde von PostgreSQL als Ende eines Strings interpretiert werden und zu einer Fehlermeldung führen. Bei PHP vor 4.2.0 müssen Sie die Funktion string addslashes ( string str) verwenden. |
$resnr = pg_query ("select currval('".$tab."_id_seq');"); | |
Dieses Kommando liest den aktuellen Wert der Sequenz tabellenname_id_seq aus. |
$nr = pg_fetch_result ($resnr, 0, 0); | |
| Um einzelne Werte aus einer Ergebnismenge zu extrahieren, wird die Funktion mixed pg_fetch_result (resource result, int row_number, mixed field) verwendet. Die Funktion hat drei Parameter: die Ergebniskennung, die Zeilennummer und das Feld der Ergebnismenge. Das Feld kann mit seinem nummerischen Index oder dem Feldnamen angegeben werden. In älteren PHP-Versionen heisst die Funktion pg_result(). |
| 7. Ein Eingabeformular |
<?php
include_once("mz_lib.php");
echo oben("Mitfahrzentrale - Fahrt eintragen");
if ($go) {
// Sind die Felder ausgefuellt?
if (!$start) {
$msg .= " Sie haben den Startort vergessen.<br>";
}
if (!$ziel) {
$msg .= " Sie haben keinen Zielort angegeben.<br>";
}
if (!$datum) {
$msg .= " Sie haben das Datum der Reise nicht angegeben.<br>";
}
if (!$pl) {
$msg .= " Sie haben nicht angegeben,
wieviele Plätze Sie anbieten.<br>";
}
// Ein registrierter Benutzer
if ($kdnr && $pwd) {
if (!kdcheck('fahrer', $kdnr, $pwd)) {
$msg .= " Zu Ihrer Kundenummer wurde kein
Eintrag in der Datenbank gefunden.
Bitte tragen Sie sich neu ein.<br>";
}
}
elseif ($emailneu && $pwdneu){
// Ein neuer Benutzer
if (pwdcheck('fahrer', $pwdneu)) {
$msg .= " Das Kennwort ist bereits vorhanden,
bitte geben Sie ein neues ein.<br>";
}
if (mailcheck('fahrer', $emailneu)) {
$msg .= " Die Mailadresse ist bereits vorhanden,
bitte benutzen Sie Ihre Kundennummer.<br>";
}
}
else {
$msg .= " Sie haben sich nicht korrekt angemeldet.<br>";
}
//-------------------------------*/
if (!$msg) {
// Das Formular ist korrekt ausgefuellt
if ($emailneu && $pwdneu){
$kdnr = neueintrag('fahrer', $emailneu, $pwdneu);
}
// Fahrt eintragen
$sql = "insert into fahrt (start, ziel, plaetze, datum, ";
if ($raucher) {
$sql .= "raucher, ";
}
$sql .= "fahrer) values ('".pg_escape_string ($start)."',";
$sql .= "'".pg_escape_string ($ziel)."', $pl, '$datum',";
if ($raucher) {
$sql .= "$raucher,";
}
$sql .= " $kdnr);";
$res = pg_query ($sql);
} // keine msg
}
?>
<table width="100%" cellpadding="5" cellspacing="0" border="0">
<tr>
<td bgcolor="#407e8c"><font size="+2" color="#ffffff"
face="verdana,arial">
<b>Die Mitfahrzentrale</b></font>
<a href="start.php">Startseite</a>
<a href="fahrten.php">Alle Fahrten</a>
<a href="startziel.php.php">Fahrt buchen</a></td>
</tr>
<tr>
<?php
// falls ein Fehler aufgetreten ist, soll eine Meldung
// in einer Tabellenzeile ausgegeben werden
if ($msg) {
echo '<td bgcolor="#7eb5c7"> ';
if (!$ok) {
echo "<strong>FEHLER:</strong><br>";
}
echo $msg.'</td>';
}
else {
echo '<td bgcolor="#7eb5c7"> ';
echo 'Eine Fahrt anbieten</td>';
}
?>
</tr></table>
<table width="100%"
cellpadding="5" cellspacing="0" border="0" bgcolor="#d3e7eb">
<form action="<?php echo $PHP_SELF; ?>" method="POST">
<tr><td>Wenn Sie schon als Fahrer bei uns eingetragen sind,
können Sie sich mit Ihrer Kundennummer und Ihrem Kennwort
identifizieren.</td>
<td valign="bottom">
<input type="text" name="kdnr" size=5 maxlength=10
value="<?php if($kdnr) {print $kdnr;} ?>">
<input type="password" name="pwd" size=15 maxlength=15
value="<?php if($pwd) {print $pwd;} ?>"></td></tr>
<tr><td>Falls Sie zum ersten Mal eine Fahrt anbieten,
geben Sie bitte Ihre E-mail-Adresse an </td>
<td valign="bottom">
<input type="text" name="emailneu" size=30 maxlength=50
value="<?php if ($emailneu) {print $emailneu;} ?>"></td></tr>
<tr><td>und ein Passwort. Das Passwort muss mindestens
6 Zeichen und höchstens 15 Zeichen lang sein.</td>
<td><input type="password" name="pwdneu" size=10 maxlength=15
value="<?php if($pwdneu) {print $pwdneu;} ?>"></td></tr>
<tr><td bgcolor="#d3e7eb" colspan="2"> </tr>
<tr><td colspan="2" bgcolor="#7eb5c7"> Ihr Mitfahrangebot</td>
</tr>
<tr><td align="right">Von: </td>
<td><input type="text" name="start" size="20"
value="<?php if($start) {print $start;} ?>"></td></tr>
<tr><td align="right">Nach: </td>
<td><input type="text" name="ziel" size="20"
value="<?php if($ziel) {print $ziel;} ?>"></td></tr>
<tr><td align="right">Datum: </td>
<td><input type="text" name="datum" size="10"
value="<?php if($datum) {print $datum;} ?>"> Format:
TT.MM.JJJJ</td></tr>
<tr><td align="right">Raucher: </td>
<td>ja
<input type="radio" name="raucher" value="TRUE"
<?php if($raucher=="ja") {print " checked";} ?> > nein
<input type="radio" name="raucher" value="FALSE"
<?php if($raucher=="no") {print " checked";} ?>> </td></tr>
<tr><td align="right">freie Plätze:</td>
<td><input type "text" name="pl" size=1 maxlength=1
value="<?php if ($pl) {print $pl;} ?>"></td></tr>
<tr><td> </td><td><input type="submit"
name="go" value="Angebot absenden"></td></tr>
</form></table>
<?php
echo unten();
?> | |
 |
| 8. Den Inhalt der Tabelle fahrt auflisten |
| Angenommen, diese Seite wird mit einem Parameter $mode aufgerufen, der angibt, nach welchem Feld der Inhalt der Tabelle zur Ausgabe sortiert werden soll. |
<table width="100%" cellpadding="5" cellspacing="0" border="0"
bgcolor="#d3e7eb">
<tr><td bgcolor="#d3e7eb" colspan="5"> </tr>
<tr>
<?php
$sql = "select start, ziel, datum, plaetze, case ";
$sql .= "when raucher = 't' then 'Raucher' ";
$sql .= "when raucher = 'f' then 'Nichtraucher' ";
$sql .= "else 'Keine Angabe' end as raucher ";
$sql .= "from fahrt where plaetze > 0 order by $mode;";
$res = @pg_query ($sql);
$cols = pg_num_fields ($res);
$rows = pg_num_rows ($res);
for ($i = 1; $i < $cols; $i++) {
// ale Feldnamen bis auf id ausgeben
echo '<th align="left">'.pg_field_name ($res,$i).'</th>';
}
echo '</tr>';
for ($j = 0; $j < $rows; $j++) {
echo '<tr>';
$arr = pg_fetch_array ($res, $j, PGSQL_NUM);
for ($i = 1; $i < $cols; $i++) {
// alle Felder bis auf id ausgeben
echo '<td>'.$arr[$i].'</td>';
}
echo '</tr>';
}
?>
<tr><td bgcolor="#d3e7eb" colspan="5"> </td>
</tr></table> | |
| Das SQL-Kommando zum Auslesen der Tabelle enthält mit |
CASE WHEN bedingung THEN ausdruck1
[WHEN bedingung THEN ausdruck2]
[ELSE ausdruck3]
END | |
ein Konstrukt, mit dem die Werte der Ergebnismenge direkt in der Abfrage von einer Bedingung abhängig gemacht werden. Wenn der Wert des Feldes raucher 't' enthält, soll das entsprechende Feld in der Ergebnismenge den Wert 'Raucher' enthalten. Hier wird die CASE-WHEN-Klausel dazu benutzt, die Feldinhalte der Ergebnismenge so zu verändern, dass der Inhalt der Ergebnismenge in zwei ineinander geschachtelten Schleifen direkt und ohne weiteren Programmieraufwand ausgegeben werden kann. Dazu ermittelt die Funktion pg_num_fields() die Anzahl der Spalten und pg_num_rows() die Anzahl der Zeilen der Ergebnismatrix. Alle Werte der Matrix werden dann innerhalb zweier FOR-Schleifen ausgegeben. |
| Die Funktion array pg_fetch_array (resource result, int row [, int result_type]) liest eine Zeile der Ergebnismenge in ein Array ein, in dem die Felder einer Ergebniszeile standardmäßig über ihren nummerischen Feldindex oder mit ihrem Feldnamen angesprochen werden. Der Parameter PSQL_NUM gibt an, dass das Array nur einen nummerischen Index hat, PSQL_ASSOC gibt ein assoziatives Array mit den Feldnamen als Indizes zurück und PSQL_BOTH liefert ein Array mit beiden Indizes. Ähnlich ist die Funktion array pg_fetch_row (resource result, int row) die eine Zeile einer Ergebnismenge in ein nummerisches Array einliest und der Funktion pg_fetch_array (result, zeile, PGSQL_NUM) entspricht. |
| * * * |
| Mehr zu PHP, PostgreSQL und seinen objektrelationalen Features können Sie ab Sommer 2002 in meinem Buch nachlesen: PostgreSQL, ISBN 3-89864-175-9. |
| Die Prototypen der PHP-Funktionen wurden mit Erlaubnis von Dr. Egon Schmid aus dem PHP Handbuch übernommen. Das Layout und die Umsetzung dieses Artikels als HTML-Dokument hat Norbert Pfeiffer übernommen. Hakan Kücükyilmaz hat einige hilfreiche Tipps beigesteuert. Allen Dreien ein herzliches Dankeschön. |
© 05.2002 by cornelia boenigk |