Datenbanken


 ... [ xml2html-Tutorial ] ... [ << Erweiterung um TCL-Skripte ] ... [ Link-Sammlung >> ] ...    ... [ Referenz ] ... 




Übersicht: Datenbanken

  Allgemeines zur Datenbank Nutzung
  Datenbank-Abfragen
  Datenbank-Änderungen



Allgemeines zur Datenbank Nutzung

Die Datenbank-Schnittstelle des xml2html-Paketes stellt die Verknüpfung zwischen xml-Daten und relationalen Datenbanken auf eine einfache Art und Weise zur Verfügung. Der Zugriff auf die relationale Datenbank wird mittels einiger einfacher xml-Tags realisiert, mit denen wiederum xml-Code generiert wird. Mit dieser Methode lassen sich einfache und komplexe Datenbankabfragen realisieren und sogar Operationen zum Einfügen, Ändern und Löschen einzelner Datensätze sind so möglich.

Die aktuelle Datenbank-Schnittstelle basiert auf einer Version von Niclas Meier. Die bisherige Schnittstelle inkl. der Dokumentation steht natürlich weiterhin zur Verfügung.

xml2html unterscheidet 2 Arten von Datenbank Anfragen. Erstens die (nur lesenden) Abfragen von Tabellendaten und zweitens das Verändern von Inhalten, also der schreibende Zugriff auf eine Tabelle. Die Datenbank-Funktionalität wird über die beiden Anweisungen:

<tcl source="sql.so" type="LOAD">
<tcl source="database.tcl">

geladen.

Für den Zugriff auf die Datenbank sind einige weitere Informationen, wie der Name der Datenbank oder der Datenbank-User, notwendig. Diese Informationen müssen xml2html durch Variablen zur Verfügung gestellt werden. Hierfür ist es notwendig, folgende Variablendeklarationen sowie das Laden der Funktionalität in jede xml-Seite zu kopieren, die Datenbank Zugriff erhalten soll. Natürlich können die Werte auch in einem Style-File deklariert werden, welches von den xml-Seiten eingebunden wird.

<set var="DBUSER" value="root">
<set var="DBNAME" value="xml2html">
<set var="DBHOST" value="localhost">
<set var="DBPASS" value="">

Sollen die folgenden Beispiele auf dieser Seite ausgeführt werden, muss es also eine Datenbank und einen User mit den oben genannten Werten geben. Diese Beispiel-Konfiguration - root-Benutzer ohne password - ist nicht für den ernsthaften Gebrauch bestimmt. Sie müssen in diesem Beispiel in der Datei man_db.style modifiziert werden.


Verwendete Tabellen
Alle Beispiele beziehen sich dabei auf drei Beipieltabellen die mit diesem Skript erzeugt werden können. Der Inhalt der Tabellen:

Tabelle Person:
+------+-----------+------------+---------+
| p_id | p_vorname | p_nachname | p_alter |
+------+-----------+------------+---------+
|    1 | gustav    | krause     |      34 |
|    2 | egon      | hahn       |      45 |
|    3 | karl      | lehmann    |      35 |
|    4 | juergen   | hartmann   |      65 |
|    5 | richard   | maier      |      33 |
|    6 | karl      | krause     |      50 |
+------+-----------+------------+---------+

Tabelle Kind:
+------+-----------+------------+---------+----------+
| k_id | k_vorname | k_nachname | k_alter | k_ort    |
+------+-----------+------------+---------+----------+
|    1 | franz     | maier      |      35 | hamburg  |
|    2 | petra     | lehmann    |       5 | berlin   |
|    3 | thomas    | hahn       |      12 | muenchen |
|    4 | britta    | lehmann    |      16 | berlin   |
|    5 | andreas   | krause     |      45 | muenchen |
+------+-----------+------------+---------+----------+

Tabelle Adresse:
+------+------+---------------+--------+----------+
| a_id | p_id | strasse       | hausnr | ort      |
+------+------+---------------+--------+----------+
|    1 |    1 | hauptstrasse  |      2 | wedel    |
|    2 |    2 | nebenweg      |     23 | hamburg  |
|    3 |    3 | marienstrasse |    124 | muenchen |
|    4 |    5 | nordtor       |     11 | muenchen |
+------+------+---------------+--------+----------+

Die Makros für die Ausgabeformatierungen sind in der Datei man_db.style definiert.




Datenbank-Abfragen

Für das Abfragen von Daten ist der <sql-query>-Tag zuständig. Der Tag darf daher nur eine select-Anfrage enthalten, ein insert oder delete ist mit diesem Tag nicht möglich.

<sql-query stmt="select p_vorname, p_nachname from person order by p_nachname;"
        ressection="s_person_name" resentry="r_person_name" resempty="person_leer" reserror="person_error">
</sql-query>

Der Parameter stmt enthält die auszuführende Anfrage, die weiteren Parameter dienen der Formatierung des Ergebnisses. ressection stellt dabei das äussere Gerüst der Darstellung da, mit resentry werden die einzelnen Ergebniszeilen der Anfrage formatiert.

<define name="S_PERSON_NAME"
        effrows=""
        body='$EFFROWS Zeilen gefunden<br><table>'
        end='</table>'
>
<define name="R_PERSON_NAME"
        p_vorname=""
        p_nachname=""
        body='<tr><td>$P_VORNAME</td><td>$P_NACHNAME</td></tr>'
>

ressection enthält in diesem Beispiel nur eine einfache Tabelle und zusätzlich die Information wieviele Zeilen ($EFFROWS) die Ergebnismenge enthält. Mit resentry können die einzelnen Ergebniszeilen der Anfrage formatiert werden. Auf die einzelnen Spalten kann dabei über die angegebenen Namen in der sql-Anfrage zugegriffen werden.

resempty und reserror dienen der Darstellung einer leeren Ergebnismenge bzw. eines Fehlers. Wird einer der Parameter nicht angegeben, geht xml2html davon aus, dass ein Makro mit dem entsprechenden Parameternamen existiert. Die Verarbeitung erfolgt hierbei also analog zum toc-Tag.

Die oben genannte Anfrage würde, vorausgesetzt die Datenbank ist installiert und der Datenbankserver läuft, somit folgendes Ergebnis liefern:





Leere Ergebnismengen und fehlerhafte Anfragen

Die folgenden Beispiele demonstrieren die Arbeitsweise des Datebankzugriffs nur dann korrekt, wenn die Datenbank überhaupt zugreifbar ist. Ist dieses nicht der Fall, so erscheint immer die gleiche Fehlermeldung.

Von diesen Seiten gibt es eine statische Version, in dieser kann man die Wirkungsweise der Anfragen erkennen, auch wenn die Datenbank nicht installiert ist.

Eine Anfrage

<sql-query stmt="select p_nachname from person where p_alter = 10;" resempty="person_leer"></sql-query>

die keine Ergebniszeilen liefert, würde das in resempty angegebene Makro ausführen, in diesem Fall: person_leer

Database not open
invalid command name "sql"

Eine fehlerhafte Anfrage

<sql-query stmt="select p_nachname from personen where p_alter = 10;" resempty="person_leer"></sql-query>

würde diese Ergebnis liefern:

Database not open
invalid command name "sql"

Bei der Definition des "Fehlermakros" kann auf zwei Parameter zugegriffen werden, usermsg und systemmsg. usermsg stellt dabei eine vom Entwickler der Schnittstellen definierte Fehlermeldung da, systemmsg wird vom Datenbank System generiert.

<define name="RESERROR"
        usermsg=""
        systemmsg=""
        body='<red>$USERMSG<br>$SYSTEMMSG</red>'
>




* Anfragen
Natürlich sind auch "select * from .." Anfragen möglich. Der Zugriff auf die einzelnen Spaltenwerte erfolgt hierbei ebenfalls über den Spaltennamen in der Tabelle.

<sql-query
   stmt="select * from kind where k_alter > 10 order by k_alter;"
   resentry="r_kind_all"
   ressection="s_kind_all"></sql-query>

Database not open
invalid command name "sql"



Unterabfragen
Einige Datenbanken, wie z.B. MySQL lassen leider keine Unterabfragen zu. xml2html ist allerdings in der Lage diesen Nachteil zu beheben und stellt dafür den <sql-subquery>-Tag zur Verfügung.

<sql-query
  stmt="select * from person where p_alter > all <subquery> order by p_nachname;"
  resentry="r_person_all"
  ressection="s_person_all"
  resempty="person_leer"
>
   <sql-subquery stmt="select k_alter from kind;"></sql-subquery>
</sql-query>

Um eine Unterabfrage ausführen zu können sind zwei Schritte notwendig.

  1. Definieren einer Anfrage in einem <sql-query>-Tag. Die Anfrage muss dabei an der Stelle an der sich normalerweise die Unterabfrage befindet, dass Schlüsselwort <subquery> enthalten.
  2. Definieren einer <sql-subquery> Anfrage die in den <sql-query>-Tag geschachtelt sei muss.

Das Ergebnis der Anfrage wäre dann:

Database not open
invalid command name "sql"

Unterabfragen lassen sich natürlich auch wiederum beliebig tief schachteln. Es ist also eine Anfrage die eine Unterabfrage enthält, die eine Unterabfrage enthält, ... möglich.

<sql-query
  stmt="select * from person where p_alter in <subquery> order by p_nachname;"
  resentry="r_person_all"
  ressection="s_person_all"
  resempty="person_leer"
>
   <sql-subquery
     stmt="select k_alter from kind where k_ort in <subquery>;">
       <sql-subquery
         stmt="select ort from adresse;"></sql-subquery>
   </sql-subquery>
</sql-query>

Das Ergebnis der Anfrage wäre dann:

Database not open
invalid command name "sql"

Lässt die Datenbank Unterabfragen zu, ist der Umweg über den <sql-subquery>-Tag natürlich nicht nötig. Bei diesen Datenbanken kann die Unterabfrage direkt im <sql-query>-Statement angegeben werden.


Vereinfachen der Makrodefinition
Da bei der Definition eines "Zeilenmakros" die Spaltennamen der Tabelle angegeben werden müssen, kann es sehr schnell zu Codeverdopplung kommen, wenn die Ergebnisse zweier unterschiedlicher Tabellen gleich dargestellt werden sollen.

<define name="KIND_ZEILE"
        k_vorname=""
        k_nachname=""
        body='<tr><td><b><italic>$K_VORNAME</italic></b></td><td><b><italic>$K_NACHNAME</italic></b></td></tr>'
>
<define name="PERSON_ZEILE"
        p_vorname=""
        p_nachname=""
        body='<tr><td><b><italic>$P_VORNAME</italic></b></td><td><b><italic>$P_NACHNAME</italic></b></td></tr>'
>

Die beiden Makros unterscheiden sich nur in den Namen der Tabellenspalten.

Durch die Definition einer Zwischenstufe kann diese Verdopplung allerdings vermieden werden. Hierbei wird ein komplexes "Standardmakro" definiert (im Beispiel: STANDARD_ZEILE) in dem die Spaltennamen der Tabelle nicht mehr vorkommen. Jetzt muss nur noch für jede Tabelle/Abfrage ein einfaches Makro geschrieben werden, welches die Umbennenung der Spaltennamen in die Standardnamen übernimmt und das komplexe Makro aufruft.

<define name="KIND_ZEILE"
        k_vorname=""
        k_nachname=""
        body='<standard_zeile v1="$K_VORNAME" v2="$K_NACHNAME">'
>
<define name="PERSON_ZEILE"
        p_vorname=""
        p_nachname=""
        body='<standard_zeile v1="$P_VORNAME" v2="$P_NACHNAME">'
>
<define name="STANDARD_ZEILE"
        v1=""
        v2=""
        body='<tr><td><b><italic>$V1</italic></b></td><td><b><italic>$V2</italic></b></td></tr>'
>






Datenbank-Änderungen

Für das Verändern von Tabelleninhalten wird der <sql-write>-Tag verwendet. Mit ihm lassen sich Datensätze hinzufügen, löschen und ändern.

<sql-write stmt="insert into kind values (0,'anja','mueller',4,'hamburg');" reswrite="resinsert"></sql-write>

<sql-write stmt="delete from kind where k_vorname= 'anja';"></sql-write>

<sql-write stmt="update kind set k_vorname='schulze' where k_vorname='lehmann';" reswrite="resupdate"></sql-write>

Der <sql-write>-Tag hat die Parameter stmt sowie reswrite und reserror. Das Makro reswrite wird ausgeführt, wenn die Änderung erfolgreich war, reserror im Fehlerfalle. Die Angabe dieser beiden Parameter kann wie im <sql-query>-Tag weggelassen werden, dann wird durch xml2html nach einem Makro mit dem Namen reswrite bzw. reserror gesucht.

Ausgaben der oberen Änderungsanfragen wären:

insert: Database not open
invalid command name "sql"

delete: Database not open
invalid command name "sql"

update: Database not open
invalid command name "sql"






 ... [ xml2html-Tutorial ] ... [ << Erweiterung um TCL-Skripte ] ... [ Link-Sammlung >> ] ... [ nach oben ] ...   ... [ Referenz ] ...