Einfache SQL-Hilfe von Christoph Reeg
zurück
 

SQL-Befehle


SELECT

zurück  
Der Befehl SELECT ist der mächtigste Befehl in SQL. Die Grund-Syntax lautet:

SELECT [DISTINCT | ALL] select_expression,... FROM tables ...
[WHERE where_definition]
[GROUP BY feld_name,...]
[ORDER BY feld_name [ASC | DESC] ,...]
[LIMIT [offset,] rows]

Die kürzestmögliche SELECT-Anweisung lautet:

SELECT * FROM table

Es sollen z.B. alle Mitarbeiter ausgegeben werden:

mysql> select * from Mitarbeiter;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   2 |    1 |     1 | junetz.de      | 1998-03-05 | 069/764758   |
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
|   4 |    3 |     1 | JCP            | NULL       | 069/764758   |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
+-----+------+-------+----------------+------------+--------------+
6 rows in set (0.00 sec)

mysql>

DISTINCT und ALL sind exklusive, optionale Parameter; soll heißen, es kann immer nur einer, muß aber keiner benutzt werden. DISTINCT sorgt dafür, daß jede identische Zeile nur einmal ausgegeben wird. Mit ALL werden die sich wiederholenden Werte auch mehrmals ausgegeben. Ohne Parameter verhält sich das DBMS normalerweise, als ob man ALL angeben würde.

Es sollen alle Telefonnummern aus der Mitarbeiter-Tabelle ausgeben werden:


mysql> SELECT Telefon from Mitarbeiter;
+--------------+
| Telefon      |
+--------------+
| NULL         |
| 069/764758   |
| NULL         |
| 069/764758   |
| 06196/671797 |
| 069/97640232 |
+--------------+
6 rows in set (0.01 sec)

mysql> SELECT ALL Telefon from Mitarbeiter;
+--------------+
| Telefon      |
+--------------+
| NULL         |
| 069/764758   |
| NULL         |
| 069/764758   |
| 06196/671797 |
| 069/97640232 |
+--------------+
6 rows in set (0.00 sec)

mysql> SELECT DISTINCT Telefon from Mitarbeiter;
+--------------+
| Telefon      |
+--------------+
| NULL         |
| 06196/671797 |
| 069/764758   |
| 069/97640232 |
+--------------+
4 rows in set (0.05 sec)

mysql>

ORDER BY

zurück  
Mit ORDER BY wird festgelegt, nach welcher Spalte bzw. welchen Spalten sortiert werden soll. Mit ASC werden die Zeilen aufsteigend, mit DESC absteigend sortiert. Ist nichts angegeben, wird aufsteigend sortiert.

Als Beispiel alle Mitarbeiter, nach Namen sortiert:

mysql> SELECT * from Mitarbeiter ORDER BY Name;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   4 |    3 |     1 | JCP            | NULL       | 069/764758   |
|   2 |    1 |     1 | junetz.de      | 1998-03-05 | 069/764758   |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
+-----+------+-------+----------------+------------+--------------+
6 rows in set (0.00 sec)

mysql> SELECT * from Mitarbeiter ORDER BY Name ASC;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   4 |    3 |     1 | JCP            | NULL       | 069/764758   |
|   2 |    1 |     1 | junetz.de      | 1998-03-05 | 069/764758   |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
+-----+------+-------+----------------+------------+--------------+
6 rows in set (0.01 sec)

mysql> SELECT * from Mitarbeiter ORDER BY Name DESC;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   2 |    1 |     1 | junetz.de      | 1998-03-05 | 069/764758   |
|   4 |    3 |     1 | JCP            | NULL       | 069/764758   |
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
+-----+------+-------+----------------+------------+--------------+
6 rows in set (0.00 sec)

mysql>

Als letztes soll nach dem Geburtsdatum sortiert werden.

mysql>  SELECT * from Mitarbeiter ORDER BY GebDat;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
|   4 |    3 |     1 | JCP            | NULL       | 069/764758   |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   2 |    1 |     1 | junetz.de      | 1998-03-05 | 069/764758   |
+-----+------+-------+----------------+------------+--------------+
6 rows in set (0.01 sec)

mysql>
Die ersten vier Mitarbeiter haben kein Geburtsdatum eingetragen. Um sie dennoch irgendwie zu sortieren, ist bei ihnen ein zweites Sortierkriterium notwendig. Das kann einfach mit einem Komma getrennt hinter ORDER BY geschrieben werden.

Um zum Beispiel nach Geburtsdatum und, wenn das nicht eindeutig ist, dann nach Namen zu sortieren, ist folgende Anweisung notwendig:


mysql>  SELECT * from Mitarbeiter ORDER BY GebDat,Name;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   4 |    3 |     1 | JCP            | NULL       | 069/764758   |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   2 |    1 |     1 | junetz.de      | 1998-03-05 | 069/764758   |
+-----+------+-------+----------------+------------+--------------+
6 rows in set (0.00 sec)

mysql>

GROUP BY

zurück  
Die GROUP BY-Anweisung wird nur in Verbindung mit den Gruppenfunktionen benutzt, um mehrere Tupel mit identischen Attributen zu Gruppen zusammenzufassen.

Ein kleines Beispiel:
Es soll ausgegeben werden, wie viele Mitarbeiter in den jeweiligen Abteilungen arbeiten.

mysql> SELECT count(*), AbtNr from Mitarbeiter GROUP BY AbtNr;
+----------+-------+
| count(*) | AbtNr |
+----------+-------+
|        3 |     1 |
|        2 |     2 |
|        1 |     3 |
+----------+-------+
3 rows in set (0.00 sec)

mysql>
Die sechs Tupel (Datensätze), die wir in der Mitarbeiter-Tabelle haben, werden zu drei Gruppen zusammengefaßt; anschließend wird die Anzahl der Tupel pro Gruppe ausgeben.

Eigentlich dürfen in der select_expression nur Spalten angegeben werden, die in GROUP BY auftauchen, MySQL ignoriert dies allerdings. Folgende Anweisung wäre eigentlich unzulässig, zudem ist sie völlig sinnlos - was besagt schon die Spalte `Name`?

mysql> SELECT Name,count(*),AbtNr from Mitarbeiter GROUP BY AbtNr;
+----------------+----------+-------+
| Name           | count(*) | AbtNr |
+----------------+----------+-------+
| junetz.de      |        3 |     1 |
| Maier          |        2 |     2 |
| Christoph Reeg |        1 |     3 |
+----------------+----------+-------+
3 rows in set (0.00 sec)

mysql>

LIMIT

zurück  
Mit LIMIT [offset,] rows kann angegeben werden, wie viele Zeilen man maximal von der SELECT-Anweisung zurückgeliefert haben will. Mit `offset` kann man festlegen, ab welcher Zeile angefangen werden soll. Ohne Angaben wird 0 angenommen. Mit `rows` legt man fest, wie viele Zeilen man maximal ausgegeben haben möchte.
mysql> select * from table LIMIT 5,10;  # gibt die Zeilen 6-15 zur�ck
mysql> select * from table LIMIT 5;     # gibt die ersten 5 Zeilen zur�ck
mysql> select * from table LIMIT 0,5;   # dasselbe nochmal

select_expression

zurück  
Für select_expression muß eingesetzt werden, was angezeigt werden soll. Im einfachsten Fall ist das ein `*`. Damit wird einfach alles angezeigt.

Im Normalfall werden die benötigten Spalten durch Kommas getrennt angegeben. Sobald die Spaltennamen nicht mehr eindeutig sind, weil mehrere Tabellen angegeben wurden, muß der Tabellenname, gefolgt von einem Punkt, vorangestellt werden; es können dabei auch sog. Aliase verwendet werden. Mehr dazu weiter unten.

Neben den reinen Spaltenwerten können auch Werte, die aus den Spaltenwerten berechnet oder durch sonstige Funktionen erstellt wurden, ausgegeben werden. Funktionen werden im Kapitel Funktionen ausführlich beschrieben.

Als Beispiel sollen der Mitarbeitername und die Telefonnummer ausgegeben werden. Die beiden Ausgaben sind äquivalent, wie man unschwer erkennen kann. Solange nur aus einer Tabelle ausgelesen wird, ist die erstere allerdings eher empfehlenswert, da nicht zu umständlich.

mysql> select Name, Telefon from Mitarbeiter;
+----------------+--------------+
| Name           | Telefon      |
+----------------+--------------+
| Christoph Reeg | NULL         |
| junetz.de      | 069/764758   |
| Uli            | NULL         |
| JCP            | 069/764758   |
| Maier          | 06196/671797 |
| Meier          | 069/97640232 |
+----------------+--------------+
6 rows in set (0.02 sec)

mysql> select Mitarbeiter.Name, Mitarbeiter.Telefon from Mitarbeiter;
+----------------+--------------+
| Name           | Telefon      |
+----------------+--------------+
| Christoph Reeg | NULL         |
| junetz.de      | 069/764758   |
| Uli            | NULL         |
| JCP            | 069/764758   |
| Maier          | 06196/671797 |
| Meier          | 069/97640232 |
+----------------+--------------+
6 rows in set (0.00 sec)

mysql>

Alias

zurück  
Alias bedeutet so viel wie ein ,,anderer Name``. Man kann sowohl für Spalten als auch für Tabellen Aliase definieren.

Tabellen-Alias

zurück  

Tabellen-Alias Tabellen-Aliase können sowohl bei der select_expression als auch bei der `where_definition` zur eindeutigen Spaltenbeschreibung anstelle des Tabellennamens verwendet werden. Aliase werden verwendet, weil sie in der Regel kürzer sind als der Spaltenname. Aliase werden bei tables mit einer Leerstelle getrennt hinter dem Tabellennamen eingegeben.

Die folgenden zwei Anweisungen sind völlig identisch, abgesehen davon, daß erstere kürzer ist. Der einzige Unterschied liegt in den ersten beiden Zeilen. Im ersten Beispiel wird bei der FROM-Anweisung ein Alias definiert, welches in der ersten Zeile bei der select_expression benutzt wird.

mysql> select M.Name, M.Telefon, M.AbtNr
    -> FROM Mitarbeiter M
    -> WHERE M.AbtNr = 1;
+-----------+------------+-------+
| Name      | Telefon    | AbtNr |
+-----------+------------+-------+
| junetz.de | 069/764758 |     1 |
| Uli       | NULL       |     1 |
| JCP       | 069/764758 |     1 |
+-----------+------------+-------+
3 rows in set (0.00 sec)

mysql> select Mitarbeiter.Name, Mitarbeiter.Telefon,
    ->        Mitarbeiter.AbtNr
    -> FROM Mitarbeiter
    -> WHERE Mitarbeiter.AbtNr = 1;
+-----------+------------+-------+
| Name      | Telefon    | AbtNr |
+-----------+------------+-------+
| junetz.de | 069/764758 |     1 |
| Uli       | NULL       |     1 |
| JCP       | 069/764758 |     1 |
+-----------+------------+-------+
3 rows in set (0.01 sec)

mysql>

Spalten-Alias

zurück  
Wenn man die Überschrift der Spalten ändern will, braucht man Spalten-Aliase. Auf den ersten Blick mag es vielleicht egal sein, wie die Spalten heißen. Später in Verbindung mit PHP ist das allerdings wichtig und sobald Funktionen in der Abfrage verwendet werden, sind die Spaltenüberschriften auch nicht mehr so schön.

Zum Umbenennen der Spalten wird einfach hinter den Spaltennamen bzw. Ausdruck der Aliasname geschrieben. Alternativ kann auch AS aliasname benutzt werden.

Ein kleines Beispiel:
Es soll die Anzahl der Mitarbeiter ausgegeben werden. Dazu wird eine Funktion benötigt (siehe Kapitel Funktionen, das soll uns aber nicht weiter stören).

mysql> SELECT count(*)
    -> FROM Mitarbeiter;
+----------+
| count(*) |
+----------+
|        6 |
+----------+
1 row in set (0.00 sec)

mysql>

Das einzige störende ist, daß die Spalte ,,count(*)`` heißt. Viel schöner wäre es doch, wenn sie z.B. ,,Anzahl`` heißen würde:
mysql> SELECT count(*) Anzahl
    -> FROM Mitarbeiter;
+--------+
| Anzahl |
+--------+
|      6 |
+--------+
1 row in set (0.00 sec)

mysql> SELECT count(*) AS Anzahl
    -> FROM Mitarbeiter;
+--------+
| Anzahl |
+--------+
|      6 |
+--------+
1 row in set (0.00 sec)

mysql>

Wie man unschwer erkennen kann, ist das AS optional.

where_definition

zurück  
Da es nicht immer sinnvoll ist, alle Zeilen auszugeben, kann man über die `where_definition` angeben, welche Bedingungen erfüllt sein müssen, damit die Zeile ausgegeben wird.

Die `where_definition` wird auch beim Löschen (DELETE) und Ändern (UPDATE) von ausgewählten Datensätzen gebraucht.

Eine Bedingung kann aus mehreren Teilbedingungen, die mit AND und OR verknüpft werden müssen, bestehen. Eine Teilbedingung besteht aus einem Spaltennamen, einem Operator sowie entweder einer Konstanten, einer weiteren Spalte oder einer Funktion. Die Teilbedingungen können auch mit NOT verneint werden. Schreibfaule können an Stelle von NOT auch einfach ! verwenden; das Ergebnis ist dasselbe. Die Reihenfolge der Teilbedingungen kann durch Klammern beeinflußt werden. Als Operatoren stehen die Vergleichsoperatoren sowie die Operatoren `LIKE`, `BETWEEN` und `IN` zur Auswahl. Alle Vergleichsoperatoren aus der Tabelle der Vergleichsoperatoren stehen zur Verfügung. Bei Vergleichen mit Strings (=VARCHAR) wird im Normalfall nicht auf die Groß-/Kleinschreibung geachtet. Wenn man jedoch unterscheiden will, so muß beim Anlegen der Tabelle bei VARCHAR die Option BINARY angegeben werden.




Tabelle 6.3: Verfügbare Vergleichsoperatoren in SQL
Operator Bedeutung verneinender Operator
= gleich <> bzw. !=
<> oder != ungleich =
> größer <=
< kleiner >=
>= größer gleich <
<= kleiner gleich >

Es sollen alle Mitarbeiter ausgegeben werden, bei denen die Abteilungsnummer größer als 2 ist:

mysql> SELECT Name, AbtNr
    -> FROM Mitarbeiter
    -> WHERE AbtNr > 2;
+----------------+-------+
| Name           | AbtNr |
+----------------+-------+
| Christoph Reeg |     3 |
+----------------+-------+
1 row in set (0.00 sec)

mysql> SELECT Name, AbtNr
    -> FROM Mitarbeiter
    -> WHERE NOT (AbtNr < 3);
+----------------+-------+
| Name           | AbtNr |
+----------------+-------+
| Christoph Reeg |     3 |
+----------------+-------+
1 row in set (0.00 sec)

mysql>

Es sollen alle Mitarbeiter ausgegeben werden, die der Abteilung 1 angehören und als Vorgesetztennummer ebenfalls die 1 haben.

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE AbtNr = 1
    ->   AND VNr = 1;
+-----------+-------+------+
| Name      | AbtNr | VNr  |
+-----------+-------+------+
| junetz.de |     1 |    1 |
| Uli       |     1 |    1 |
+-----------+-------+------+
2 rows in set (0.02 sec)

mysql>

Es sollen alle Mitarbeiter ausgegeben werden, die keinen Vorgesetzten haben oder der Abteilung 1 angehören:

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE AbtNr = 1
    ->    OR VNr IS NULL;
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.01 sec)

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE NOT (AbtNr <> 1)
    ->   OR VNr IS NULL;
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.00 sec)

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE NOT (AbtNr <> 1 AND VNr IS NOT NULL);
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.00 sec)

mysql>

Bei der Überprüfung auf keinen Vorgesetzten ist IS NULL verwendet worden, da bei Vergleichen mit NULL-Werten nicht mit den normalen Operatoren gearbeitet werden kann. Statt dessen ist nur IS NULL oder, verneint, IS NOT NULL möglich.

Es gibt mit Sicherheit noch 1001 andere Möglichkeiten, auf diese Lösung zu kommen. Da der erste Korrekturleser mir das nicht glauben wollte, hier noch ein paar Möglichkeiten mehr:

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE NOT (AbtNr < 1 OR AbtNr > 1)
    ->    OR VNr IS NULL;
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.00 sec)

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE (AbtNr <= 1 AND AbtNr >= 1)
    ->    OR VNr IS NULL;
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.01 sec)

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE AbtNr & 1 = 1
    ->    OR VNr IS NULL;
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.00 sec)

mysql> SELECT Name, AbtNr, VNr
    -> FROM Mitarbeiter
    -> WHERE POW(13,AbtNr) = 13
    ->    OR POW(VNr,42) IS NULL;
+----------------+-------+------+
| Name           | AbtNr | VNr  |
+----------------+-------+------+
| Christoph Reeg |     3 | NULL |
| junetz.de      |     1 |    1 |
| Uli            |     1 |    1 |
| JCP            |     1 |    3 |
+----------------+-------+------+
4 rows in set (0.02 sec)

mysql>

Es sind noch nicht 1001 Möglichkeiten, aber wenn ich vieeel Zeit habe, werde ich daran weiterarbeiten ;-). Wer sich die beiden letzten Lösungen genau angesehen hat, wird feststellen, daß dort unbekannte Befehle verwendet werden; in Kapitel Funktionen werden diese noch genauer beschrieben.

Bei der letzten Möglichkeit sieht man, was passiert, wenn mit NULL-Werten gerechnet wird: Das Ergebnis ist NULL.

LIKE

zurück  
Immer dann, wenn man in Textfeldern im Suchmuster Platzhalter oder Jokerzeichen (auch reguläre Ausdrücke genannt) verwenden will, können die Vergleichsoperatoren nicht verwendet werden. Ein Beispiel zur Verdeutlichung:

Es sollen alle Mitarbeiter ausgegeben werden, bei denen die Telefon-Vorwahl auf ,, 96`` endet. Falls du eine Möglichkeit findest, das mit Vergleichsoperatoren (und ohne Funktionen) zu lösen, schicke mir bitte eine E-Mail an die Adresse dsp@reeg.net; ich bevorzuge übrigens den Operator LIKE.

mysql> SELECT Name, Telefon
    -> FROM Mitarbeiter
    -> WHERE Telefon LIKE '%96/%';
+-------+--------------+
| Name  | Telefon      |
+-------+--------------+
| Maier | 06196/671797 |
+-------+--------------+
1 row in set (0.00 sec)

mysql>

Es soll ein Mitarbeiter mit Namen ,,Meier`` ausgegeben werden. Allerdings ist unbekannt, ob er sich mit ,,ei`` oder ,,ai`` schreibt. Es sollen also alle Möglichkeiten ausgegeben werden.

mysql> SELECT Name
    -> FROM Mitarbeiter
    -> WHERE Name LIKE 'M_ier';
+-------+
| Name  |
+-------+
| Maier |
| Meier |
+-------+
2 rows in set (0.00 sec)

mysql>

Wie wir sehen, gibt es zwei Jokerzeichen:

% DOS-Pendant: * steht für eine beliebige (auch 0) Anzahl beliebiger Zeichen
_ DOS-Pendant: ? steht für genau ein beliebiges Zeichen

BETWEEN

zurück  
Neben dem LIKE-Operator gibt es auch noch andere, zum Beispiel den BETWEEN-Operator. Er tut das, was man von ihm erwartet: er wählt alle Spalten aus, die zwischen dem oberen und unteren Wert liegen.

Beispiel: Es sollen alle Mitarbeiter ausgewählt werden, deren Abteilungs-Nummer zwischen 2 und 5 liegt. Mit dem bisherigen Wissen würde man folgende Anweisung nehmen:

mysql> SELECT * FROM Mitarbeiter
    -> WHERE AbtNr >= 2 AND AbtNr <=5;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
+-----+------+-------+----------------+------------+--------------+
3 rows in set (0.01 sec)

mysql>
Man kann es aber noch etwas vereinfachen:
mysql> SELECT * FROM Mitarbeiter
    -> WHERE AbtNr BETWEEN 2 AND 5;
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   5 |    1 |     2 | Maier          | NULL       | 06196/671797 |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
+-----+------+-------+----------------+------------+--------------+
3 rows in set (0.00 sec)

mysql>

BETWEEN kann bei Textspalten, Datumsspalten und numerischen Spalten verwendet werden.

IN

zurück  
Der Operator IN schließlich wird benutzt, wenn man nicht mit einem einzelnen Wert, sondern mit einer Wertemenge vergleichen will.

Zur Verdeutlichung: Es sollen alle Mitarbeiter ausgegeben werden, deren Telefonnummer ,,06196/671797`` oder ,,069/764758`` ist. Mit den bisherigen Operatoren würde sich folgende Abfrage ergeben:

mysql> SELECT * FROM Mitarbeiter
    -> WHERE Telefon = '06196/671797' OR Telefon = '069/764758';
+-----+------+-------+-----------+------------+--------------+
| MNr | VNr  | AbtNr | Name      | GebDat     | Telefon      |
+-----+------+-------+-----------+------------+--------------+
|   2 |    1 |     1 | junetz.de | 1998-03-05 | 069/764758   |
|   4 |    3 |     1 | JCP       | NULL       | 069/764758   |
|   5 |    1 |     2 | Maier     | NULL       | 06196/671797 |
+-----+------+-------+-----------+------------+--------------+
3 rows in set (0.00 sec)

mysql>

Das funktioniert zwar, aber da diese Möglichkeit bei großen Mengen von Werten sehr umständlich und unübersichtlich wird, hier das ganze nochmal mit dem IN-Operator:
mysql> SELECT * FROM Mitarbeiter
    -> WHERE Telefon IN ('06196/671797','069/764758');
+-----+------+-------+-----------+------------+--------------+
| MNr | VNr  | AbtNr | Name      | GebDat     | Telefon      |
+-----+------+-------+-----------+------------+--------------+
|   2 |    1 |     1 | junetz.de | 1998-03-05 | 069/764758   |
|   4 |    3 |     1 | JCP       | NULL       | 069/764758   |
|   5 |    1 |     2 | Maier     | NULL       | 06196/671797 |
+-----+------+-------+-----------+------------+--------------+
3 rows in set (0.00 sec)

mysql>
Der IN-Operator kann bei Textspalten, Datumsspalten und numerischen Spalten verwendet werden.

Die Verneinung des IN-Operators ist NOT IN. Als Beispiel sollen alle Mitarbeiter ausgegeben werden, deren Telefonnummer nicht ,,06196/671797`` oder ,,069/97640232`` ist. Erst umständlich mit OR und dann elegant...

mysql> SELECT * FROM Mitarbeiter
    -> WHERE NOT (Telefon = '06196/671797' OR Telefon = '069/764758');
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
+-----+------+-------+----------------+------------+--------------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM Mitarbeiter
    -> WHERE Telefon NOT IN ('06196/671797','069/764758');
+-----+------+-------+----------------+------------+--------------+
| MNr | VNr  | AbtNr | Name           | GebDat     | Telefon      |
+-----+------+-------+----------------+------------+--------------+
|   1 | NULL |     3 | Christoph Reeg | 1979-05-13 | NULL         |
|   3 |    1 |     1 | Uli            | NULL       | NULL         |
|   6 |    5 |     2 | Meier          | NULL       | 069/97640232 |
+-----+------+-------+----------------+------------+--------------+
3 rows in set (0.00 sec)

mysql>

Funktionen

zurück  
Bei select_expression und where_expression können neben Konstanten und Spaltenwerten auch Funktionen verwendet werden. Es gibt zwei Arten von Funktionen, zum einen die sog. ,,singlerow``-Funktionen und zum anderen die Gruppenfunktionen. Singlerow-Funktionen werden auf jede Zeile angewendet, während die Gruppenfunktionen immer auf eine Gruppe von Zeilen angewendet werden.

Es wäre zum Beispiel Schwachsinn, zu versuchen, den Betrag von allen Stellenanzahlen auf einmal zu berechnen; bei der Summe paßt das schon eher.

Mathematische Funktionen

zurück  
Es können nicht nur Spaltennamen angegeben werden, sondern auch mathematische Rechenoperationen mit Spalten und/oder Konstanten.

Eine Auswahl der mathematischen Funktionen ist in Tabelle 6.4 aufgeführt.


Tabelle 6.4: Mathematische Funktionen in SQL
+ - * / addieren/subtrahieren/multiplizieren/dividieren
% modulo (ganzzahliger Rest)
ABS() Betrag von
COS() Cosinus in rad
DEGREES() Umrechnung von rad in deg (Grad)
MOD() Modulo (ganzzahliger Rest)
PI() die Zahl Pi
POW(X,Y) rechnet X hoch Y aus
RAND() liefert eine Zufallszahl zwischen 0 und 1
ROUND() rundet Wert
ROUND(x,n) rundet Wert von x auf n Stellen
SQRT() Wurzel (2. Grades)
TRUNCATE(x,n) schneidet nach n Kommastellen von x ab


Logische Operatoren

zurück  
Die logischen bzw. booleschen Operatoren haben wir bereits im Kapitel where-definition kennengelernt. In Tabelle 6.5 sind sie der Vollständigkeit halber noch einmal aufgeführt.


Tabelle 6.5: Logische Funktionen in SQL
NOT logisches NOT. Wird genau dann wahr (gibt 1 zurück), wenn
! das Argument 0 ist (sonst Rückgabe 0). NOT NULL gibt NULL zurück.
AND logisches UND. Wird genau dann wahr (gibt 1 zurück), wenn
&& alle Argumente weder 0 noch NULL sind (sonst Rückgabe 0).
OR logisches ODER. Wird genau dann wahr (gibt 1 zurück), wenn
|| eines der Argumente weder 0 noch NULL ist (sonst Rückgabe 0).


Sonstige Funktionen

zurück  
In Tabelle 6.6 und 6.7 sind sonstige, teilweise praktische Funktionen aufgeführt. Die Tabellen sind nicht vollständig!


Tabelle 6.6: Bit-Funktionen in SQL
| bitweises ODER
& bitweises UND



Tabelle 6.7: String-Funktionen in SQL
CONCAT(str1, str2, ...) Gibt den String zurück, der durch Zusammenführen der Argumente entstanden ist. Sobald ein Argument NULL ist, wird NULL zurückgegeben.
LEFT(str,n) schneidet n Buchstaben von `str` ab und gibt diese zurück
LTRIM(str) löscht alle Leerzeichen am Anfang von `str`
PASSWORD(str) verschlüsselt den Klartext `str`
REVERSE(str) dreht `str` um, d.h. letzter Buchstabe ist dann am Anfang
LCASE(str) Wandelt `str` in Kleinbuchstaben
LOWER(str) und gibt das Ergebnis zurück
UCASE(str) Wandelt `str` in Großbuchstaben
UPPER(str) und gibt das Ergebnis zurück



Tabelle 6.8: Datum-Funktionen in SQL
DAYOFWEEK(date) Gibt den Wochentag-Index des Datums zurück (1 = Sonntag, 2 = Montag, ..., 7 = Samstag)
DAYOFMONTH(date) Gibt den Tag des Monats zurück
DAYOFYEAR(date) Gibt den Tag im Jahr zurück
WEEK(date) Gibt die Woche des Datums zurück.
WEEK(date,first) Wenn `first` nicht angegeben wird bzw. 0 ist, fängt die Woche mit Sonntag an. Ist `first` z.B. 1, fängt die Woche mit Montag an.
MONTH(date) Gibt den Monat zurück
YEAR(date) Gibt das Jahr zurück
DATE_FORMAT(date,format) Formatiert das Datum date entsprechend dem übergebenen format String.
UNIX_TIMESTAMP(date) Gibt den Unix-Timestamp (Sekunden seit dem 1.1.1970) des Datums date zurück.


Datums-Funktionen

zurück  
Mit Hilfe von DATE_FORMAT kann man Datumswerte aus Tabellen so formatieren, wie man sie gerne hätte. Die Funktion erwarten zwei Parameter. Zum einen das Datumsfeld, zum anderen den Formatierungs-String. Die Formatierungszeichen (siehe Tabelle 6.9) werden durch die entsprechenden Werte ersetzt. Alle anderen Zeichen werden so wie sie sind ausgegeben.

In PHP gibt es auch eine Datum-Formatierungsfunktion. Ob man nun mit der MySQL-Funktion das Datum formatiert und dann mit PHP ausgibt oder mit Hilfe der PHP-Funktion das Datum formatiert, ist häufig egal. Teilweise ist es praktischer, mit Hilfe der MySQL-Funktion das Datum zu formatieren, weil man dann die Formatierungsanweisung in der SQL-Abfrage hat und mit PHP nur ausgeben muß. Andererseits kann es aber auch praktischer sein, mit PHP zu formatieren, wenn man zum Beispiel dasselbe Datum an mehreren Stellen auf der Seite verschieden formatiert haben will. Wie gesagt, es kommt auf den Einzelfall und die Vorlieben des Programmierers an.


Tabelle 6.9: mögl. Formatierungen für DATE_FORMAT
%W Wochentag
%w Tag in der Woche (0 = Sonntag, ..., 6=Samstag)
%d Tag des Monats (00 - 31)
%e Tag des Monats (0 - 31)
%j Tag im Jahr (001 - 366)
%U Woche, mit Sonntag als 1. Tag der Woche (00 - 52)
%u Woche, mit Montag als 1. Tag der Woche (00 - 52)
%M Monatsname
%m Monat, numerisch (01 - 12)
%c Monat, numerisch (1 - 12)
%Y Jahr (4stellig)
%y Jahr (2stellig)
%T Uhrzeit (24 Std.) (hh:mm:ss)
%S Sekunden (00 - 59)
%s Sekunden (00 - 59)
%i Minuten (00 - 59)
%H Stunde (00 - 23)
%k Stunde (0 - 23)
%h Stunde (00 - 12)
%I Stunde (00 - 12)
%l Stunde (0 - 12)
%% %


Gruppenfunktionen

zurück  
Es können aber auch die sogenannten Gruppenfunktionen verwendet werden. Gruppenfunktionen heißen so, weil sie immer auf eine Gruppe von Tupeln (Datensätzen) angewendet werden.

In Verbindung mit Gruppenfunktionen darf streng genommen kein Spaltenname mehr mit in der select_expression stehen. Die einzige Ausnahme ist dann gegeben, wenn der Spaltenname in der GROUP BY-Anweisung steht. MySQL sieht das etwas lockerer und gibt keine Fehlermeldung bei Verwendung von Spaltennamen in der select_expression aus, allerdings hat dies im Normalfall wenig Sinn.

In Tabelle 6.10 sind ein Teil der verfügbaren Gruppenfunktionen aufgeführt.


Tabelle 6.10: Gruppenfunktionen in SQL
COUNT(expr) zählt die Zeilen, deren Werte ungleich NULL sind
AVG(expr) durchschnittlicher Wert
MIN(expr) kleinster Wert
MAX(expr) größter Wert
SUM(expr) Summe


Für `expr` ist immer der Name der Spalte einzusetzen, mit der diese Operation erfolgen soll.

Beispiel: Es soll ausgegeben werden, wie viele Mitarbeiter ihren Geburtstag angegeben haben.

mysql> SELECT count(GebDat)
    -> FROM Mitarbeiter;
+---------------+
| count(GebDat) |
+---------------+
|             2 |
+---------------+
1 row in set (0.27 sec)

mysql>

Joins

zurück  
Nachdem wir jetzt die Tabellen perfekt abfragen können, wollen wir mal ein paar Tabellen miteinander verbinden. Nach unserer Normalisierung (siehe Kapitel 4.4) befinden sich einige Informationen nämlich in verschiedenen Tabellen, obwohl sie eigentlich zusammengehören. Zum Beispiel würde ich gerne alle Mitarbeiter mit deren Abteilungen sehen. Hierbei aber nicht die Abteilungsnummer, sondern den Namen der Abteilung. Nach dem, was wir bis jetzt wissen, würden wir folgende Abfrage starten:
mysql> SELECT m.Name, m.AbtNr, a.Name, a.AbtNr
    -> FROM Mitarbeiter m, Abteilung a;
+----------------+-------+------------+-------+
| Name           | AbtNr | Name       | AbtNr |
+----------------+-------+------------+-------+
| Christoph Reeg |     3 | EDV        |     1 |
| junetz.de      |     1 | EDV        |     1 |
| Uli            |     1 | EDV        |     1 |
| JCP            |     1 | EDV        |     1 |
| Maier          |     2 | EDV        |     1 |
| Meier          |     2 | EDV        |     1 |
| Christoph Reeg |     3 | Verwaltung |     2 |
| junetz.de      |     1 | Verwaltung |     2 |
| Uli            |     1 | Verwaltung |     2 |
| JCP            |     1 | Verwaltung |     2 |
| Maier          |     2 | Verwaltung |     2 |
| Meier          |     2 | Verwaltung |     2 |
| Christoph Reeg |     3 | Chefetage  |     3 |
| junetz.de      |     1 | Chefetage  |     3 |
| Uli            |     1 | Chefetage  |     3 |
| JCP            |     1 | Chefetage  |     3 |
| Maier          |     2 | Chefetage  |     3 |
| Meier          |     2 | Chefetage  |     3 |
+----------------+-------+------------+-------+
18 rows in set (0.07 sec)

mysql>

Equi-Join

zurück  
Die obige Abfrage ergibt allerdings nicht ganz das erwünschte Resultat. Bei dieser Art der Abfrage entsteht nämlich das kartesische Produkt, was so viel bedeutet wie ,,jeder mit jedem``. Wie wir oben jedoch unschwer erkennen können, gehören nur die Mitarbeiter und Abteilungen zusammen, deren AbtNr übereinstimmen, deshalb hatten wir diese auch eingefügt. Eine entsprechend modifizierte Abfrage würde demnach folgendermaßen lauten:
mysql> SELECT m.Name, m.AbtNr, a.Name, a.AbtNr
    -> FROM Mitarbeiter m, Abteilung a
    -> WHERE m.AbtNr = a.AbtNr;
+----------------+-------+------------+-------+
| Name           | AbtNr | Name       | AbtNr |
+----------------+-------+------------+-------+
| junetz.de      |     1 | EDV        |     1 |
| Uli            |     1 | EDV        |     1 |
| JCP            |     1 | EDV        |     1 |
| Maier          |     2 | Verwaltung |     2 |
| Meier          |     2 | Verwaltung |     2 |
| Christoph Reeg |     3 | Chefetage  |     3 |
+----------------+-------+------------+-------+
6 rows in set (0.00 sec)

mysql>
Vor den Spaltennamen muß jeweils die entsprechende Tabelle genannt werden, da die Namen nicht eindeutig sind. Um nicht jedes Mal den kompletten Tabellennamen benutzen zu müssen, wurden Aliase verwendet (siehe Kapitel Tabellenalias).

Über die AbtNr entsteht die Verbindung zwischen den beiden Tabellen. Falls bei der Verknüpfung von mehreren Tabellen nicht das gewünschte Ergebnis erscheint, fehlt häufig eine WHERE-Bedingung, so daß bei einigen Tabellen das kartesische Produkt entsteht. In solchen Fällen kann man einfach nachzählen: Wenn n Tabellen miteinander verknüpft werden sollen, werden (n-1) WHERE-Bedingungen benötigt.

Diese Art der Verbindung wird Equi-Join genannt.

Self-Join

zurück  
So, wie man mehrere Tabellen miteinander verbinden kann, ist es auch möglich, eine Tabelle mit sich selbst zu verbinden. Notwendig ist dies in unserem Beispiel beim Vorgesetzten. Es ist zwar schön, daß man zu jedem Mitarbeiter die Mitarbeiter-Nummer seines Vorgesetzten abrufen kann, aber der Name wäre doch noch viel schöner:

mysql> SELECT m.Name, m.VNr, v.Name, v.MNr
    -> FROM Mitarbeiter m, Mitarbeiter v
    -> WHERE m.VNr = v.MNr;
+-----------+------+----------------+-----+
| Name      | VNr  | Name           | MNr |
+-----------+------+----------------+-----+
| junetz.de |    1 | Christoph Reeg |   1 |
| Uli       |    1 | Christoph Reeg |   1 |
| Maier     |    1 | Christoph Reeg |   1 |
| JCP       |    3 | Uli            |   3 |
| Meier     |    5 | Maier          |   5 |
+-----------+------+----------------+-----+
5 rows in set (0.13 sec)

mysql>

Die Tabelle `Mitarbeiter` muß zwei Mal innerhalb der FROM-Anweisung auftauchen. Um nachher die Spalten eindeutig bezeichnen zu können, müssen Tabellen-Aliase vergeben werden.

Der einzige Schönheitsfehler bei der Abfrage ist, daß der Mitarbeiter ,,Christoph Reeg`` nicht aufgelistet wird. Im Prinzip ist das logisch, da er keinen Vorgesetzten hat. Dumm ist es trotzdem, und deshalb kommen wir zur nächsten Form des Joins: dem Outer-Join.

Outer-Join

zurück  
Um beim Join alle Tupel der Haupttabelle mit den dazu passenden Tupeln der nachgeordneten Tabelle zu bekommen, wenn nicht zu jedem Tupel der Haupttabelle ein passender Tupel existiert, wird der Outer-Join benötigt.

Die Syntax unter MySQL lautet (wo sonst die Tabellennamen stehen):

     haupttabelle LEFT JOIN tabelle2 ON bedingung
oder haupttabelle LEFT JOIN tabelle2 USING (spalte)
Bei der unteren Möglichkeit müssen die Spaltennamen in den beiden Tabellen, über die die Verbindung entsteht, gleich sein. Bei der oberen Möglichkeit muß an die Stelle, wo ` bedingung` steht, das eingesetzt werden, was man beim Equi-Join als ` where_condition` schreiben würde. In beiden Fällen kann auch LEFT OUTER JOIN anstelle von LEFT JOIN geschrieben werden - das OUTER ist optional und nur aus Gründen der SQL-Kompatibilität erlaubt.

Um beim oben gezeigten Beispiel zu bleiben: Es sollen alle Mitarbeiter mit deren Vorgesetzten (sofern vorhanden) angezeigt werden. Da die Spaltennamen ungleich sind (in der Haupttabelle `VNr` und in der nachgeordneten Tabelle `MNr`), muß die obere Syntax benutzt werden.

mysql> SELECT m.Name, m.VNr, v.Name, v.MNr
    -> FROM Mitarbeiter m LEFT JOIN Mitarbeiter v ON m.VNr = v.MNr;
+----------------+------+----------------+------+
| Name           | VNr  | Name           | MNr  |
+----------------+------+----------------+------+
| Christoph Reeg | NULL | NULL           | NULL |
| junetz.de      |    1 | Christoph Reeg |    1 |
| Uli            |    1 | Christoph Reeg |    1 |
| JCP            |    3 | Uli            |    3 |
| Maier          |    1 | Christoph Reeg |    1 |
| Meier          |    5 | Maier          |    5 |
+----------------+------+----------------+------+
6 rows in set (0.03 sec)

mysql>

Die Mitarbeitertabelle mit dem Alias ,,m`` ist in unserem Fall die Haupttabelle.

Wie man unschwer erkennen kann, wird bei den Attributen, bei denen keine Werte existieren (in diesem Beispiel die beiden rechten Spalten in der ersten Zeile), NULL als Wert genommen.

Übrigens: Man muß natürlich nicht sowohl VNr als auch MNr abfragen (also zwischen SELECT und FROM angeben), da deren Werte wegen der ON-Bedingung (in der sie wiederum stehen müssen) immer gleich sind. Wenn man nur wissen möchte, in welcher Beziehung die einzelnen Mitarbeiter zu den Vorgesetzten stehen, kann man sogar ganz auf das Abfragen der Nummern verzichten. Um nicht völlig den Überblick zu verlieren, vergeben wir dabei noch Aliase:

mysql> SELECT m.Name AS Mitarb, v.Name as Vorges
    -> FROM Mitarbeiter m LEFT JOIN Mitarbeiter v ON m.VNr = v.MNr;
+----------------+----------------+
| Mitarb         | Vorges         |
+----------------+----------------+
| Christoph Reeg | NULL           |
| junetz.de      | Christoph Reeg |
| Uli            | Christoph Reeg |
| JCP            | Uli            |
| Maier          | Christoph Reeg |
| Meier          | Maier          |
+----------------+----------------+
6 rows in set (0.03 sec)

mysql>

Ein neues Beispiel:
Ich will alle Abteilungen aufgelistet haben, aber mit deren Autos.

mysql> SELECT a.Name, p.Kennzeichen, p.Typ
    -> FROM Abteilung a, PKW p
    -> WHERE a.AbtNr = p.AbtNr;
+-----------+-------------+---------+
| Name      | Kennzeichen | Typ     |
+-----------+-------------+---------+
| Chefetage | MTK-CR 1    | RR      |
| EDV       | F-JN 1      | VW-Golf |
+-----------+-------------+---------+
2 rows in set (0.04 sec)

mysql>

Wie man sieht, führt der Equi-Join nicht zum gewünschten Ergebnis. Also das ganze nochmal als Outer-Join. Da die Schlüsselspalten denselben Namen haben, können in diesem Fall beide Syntaxvarianten verwendet werden:

mysql> SELECT a.Name, p.Kennzeichen, p.Typ
    -> FROM Abteilung a LEFT JOIN PKW p ON a.AbtNr = p.AbtNr;
+------------+-------------+---------+
| Name       | Kennzeichen | Typ     |
+------------+-------------+---------+
| EDV        | F-JN 1      | VW-Golf |
| Verwaltung | NULL        | NULL    |
| Chefetage  | MTK-CR 1    | RR      |
+------------+-------------+---------+
3 rows in set (0.00 sec)

mysql> SELECT a.Name, p.Kennzeichen, p.Typ
    -> FROM Abteilung a LEFT JOIN PKW p USING (AbtNr);
+------------+-------------+---------+
| Name       | Kennzeichen | Typ     |
+------------+-------------+---------+
| EDV        | F-JN 1      | VW-Golf |
| Verwaltung | NULL        | NULL    |
| Chefetage  | MTK-CR 1    | RR      |
+------------+-------------+---------+
3 rows in set (0.00 sec)

mysql>

Als kleine Herausforderung hätte ich jetzt gerne die Abteilungen, die keinen PKW haben. Die Lösung ist eigentlich einfach. Wenn man sich das obige Ergebnis ansieht, stellt man fest (wie auch etwas weiter oben schon beschrieben), daß bei der Abteilung ohne PKW (nämlich ,,Verwaltung``) `Kennzeichen` und `Typ` NULL sind.
mysql> SELECT a.Name, p.Kennzeichen, p.Typ
    -> FROM Abteilung a LEFT JOIN PKW p USING (AbtNr)
    -> WHERE Typ IS NULL;
+------------+-------------+------+
| Name       | Kennzeichen | Typ  |
+------------+-------------+------+
| Verwaltung | NULL        | NULL |
+------------+-------------+------+
1 row in set (0.03 sec)

mysql>

Christoph Reeg  

Test