Illustration

Ob PHP, Perl, Servlets, JSP oder ASP - wer sich mit Web-Programmierung beschäftigt, wird sich früher oder später mit SQL-Datenbanken konfrontiert sehen. Die einfachen SQL-Statements sind schnell erlernt. Einsteiger übersehen häufig, dass die meisten SQL-Engines weitergehende Funktionen besitzen. Sie vereinfachen die Entwicklung und können die Performance steigern. Wie und wo zeigt folgende Artikel am Beispiel von MySQL.

Hintergrund
Die Open Source-Software MySQL ist ein Relationales Datenbank-Management-System (RDBMS). Aufgrund seiner einfachen Handhabung und Schnelligkeit wird es für den Einsatz im Rahmen kleinerer und mittlerer Projekte sehr gerne gewählt. Vor allem in der PHP-Szene ist MySQL mittlerweile de facto Standard.

MySQL versteht einen Grundstock von SQL-Statements. Sie sind allen SQL-RDBMS gemeinsam. Die wichtigsten vier sind INSERT, SELECT, DELETE und UPDATE. Daneben hat beinahe jedes RDBMS eine ganze Reihe eigener Funktionen an Bord, die innerhalb solcher Statements verwendet werden. Der Einsatz im Rahmen eines SELECT ist dabei der häufigste Fall. MySQL bietet unter anderem arithmetische, logische, mathematische sowie Datums-, String- und Kontrollfluß-Funktionen. Sie dienen dazu, bei SQL-Abfragen die Datensätze schon auf der Datenbank-Ebene zu verändern oder vorzuverarbeiten. Dadurch werden die Daten in die benötigte Form gebracht.

Anwendungsmöglichkeiten
Sehen wir uns ein einfaches Beispiel an: Eine Datenbank enthält eine Tabelle namens "kontakte", in der Vor- und Nachnamen sowie Firmenzugehörigkeiten von Personen gespeichert sind. Außerdem enthält sie ein Datumsfeld, um die letzte Änderung des Datensatzes festzuhalten.

Die MySQL-Tabelle "kontakte".

Um alle Datensätze auszulesen und nach dem Nachnamen sortiert in der Form "Vorname Nachname, Firma / Datum" auf einer Seite anzuzeigen, würde theoretisch ein simples "SELECT firma, vorname, nachname, datum FROM kontakte ORDER BY nachname" ausreichen. Die Ergebnisse werden in einer Schleife ausgegeben. Aber schon, wenn es darum geht, das Datum vom internen MySQL-Format JJJJ-MM-TT in das deutsche TT.MM.JJJJ zu transformieren, ist in diesem Fall Handarbeit angesagt. In Perl z.B. würde man, um den gewünschten Zweck zu erzielen, für jeden einzelnen Datensatz eine Operation wie "s/(\d{4})-(\d{2})-(\d{2})/$3.$2.$1/" vornehmen müssen. Noch unangenehmer wird es hingegen bei den Namen. Mittels einer Bedingung ist zu testen, ob ein Vorname eingegeben wurde; wenn ja, folgen auf den Vornamen ein Leerzeichen und der Nachname, wenn nicht, wird nur der Nachname ausgegeben. Ebenso ist die Existenz des Firmennamens zu überprüfen. Wenn vorhanden, soll dieser mit vorangestelltem Komma und Leerzeichen angezeigt werden, andernfalls ist die Firmen-Angabe zu ignorieren.

Diese Vorgehensweise ist denkbar - besonders elegant oder performant ist sie aber nicht. Wäre es nicht naheliegend, schon im Moment der Anfrage MySQL mitzuteilen, in welchem Format wir die Datensätze haben möchten? Um das oben gestellte Problem zu lösen, bedienen wir uns einiger MySQL-Funktionen:

DATE_FORMAT(Datumswert,Formatierungsstring) - dient der nahezu beliebigen Formatierung von Datums- oder Datums-Zeit-Feldern. Der Formatierungsstring funktioniert ähnlich wie "printf" in z.B. C, Perl oder PHP. Die Spezifizierer unterscheiden sich allerdings (s.u.).
IF(Ausdruck1,Ausdruck2,Ausdruck3) - funktioniert exakt wie das IF in den meisten anderen Sprachen. Ist Ausdruck1 wahr, wird Ausdruck2 eingesetzt, andernfalls Ausdruck3. Wir werden die Funktion benutzen, um zu testen, ob die Vornamen- und die Nachnamen-Felder ausgefüllt sind.
CONCAT(Ausdruck1[,...]) - verkettet alle Strings, die als Argumente übergeben werden.
LENGTH(string) - liefert die Länge eines Strings.

An der prinzipiellen Struktur des SQL-Statements ändert sich nichts - einzig die Feldnamen in "SELECT firma, vorname, nachname, datum FROM kontakte ORDER BY nachname" ersetzen wir durch geeignete Funktionen:

Statt "vorname, nachname" setzen wir die Funktion "IF(LENGTH(vorname), CONCAT(vorname, ' ', nachname), CONCAT('Mr. ', nachname)) AS name" ein. Im Klartext: Enthält das Feld "vorname" mindestens ein Zeichen, werden Vor- und Nachname durch ein Leerzeichen verbunden ausgegeben. Sonst nur der Nachname, dem wir in diesem Fall ein "Mr. " voranstellen. Korrekterweise könnte man das Geschlecht in der Datenbank speichern und per "IF" die passende Anrede einzusetzen. Aber das sparen wir uns hier der Übersichtlichkeit halber. Das nachgestellte "AS name" teilt MySQL mit, dass wir das Ergebnis dieser Operation unter dem Feldnamen "name" im Ergebnis zu finden wünschen.
Die "firma" ersetzen wir analog dazu durch den Ausdruck "IF(LENGTH(firma), CONCAT(', ' , firma), '') AS firma". Statt "datum" wählen wir den Ausdruck "DATE_FORMAT(datum, '%d.%m.%Y') AS datum". Im Formatierungsstring steht "%d" für den Monats-Tag von 00 bis 31, "%m" für den Monat von 00 bis 12, "%Y" für die vierstellige Jahresangabe; "%d.%m.%Y" ergibt also ein deutsches Datums-Format.

Das SQL-Statement lautet nun:


SELECT
	IF(LENGTH(vorname), CONCAT(vorname, ' ', 

nachname), CONCAT('Mr. ', nachname)) AS name,
	IF(LENGTH(firma), CONCAT(', ' , firma), '') 

AS firma, DATE_FORMAT(datum, '%d.%m.%Y') AS datum
	FROM kontakte
	ORDER BY nachname

(Anmerkung: Die Einrückungen und Returns dienen lediglich der Übersichtlichkeit; auch die Leerzeichen sind beliebig setzbar. Nur die Funktionen bilden hierbei eine Ausnahme, denn zwischen diesen und der öffnenden Klammer darf kein weiteres Zeichen stehen.)

Das Ergebnis der fertigen SQL-Abfrage

Da die Datensätze nun wunschgemäß vorliegen, müssen sie nur noch in einer Schleife ausgegeben werden. Selbstverständlich könnte man noch weiter gehen und die Felder mittels "CONCAT()" zu einer fertigen HTML-Tabellenzeile inklusive "<tr>"- und "<td>"-Tags zusammenfassen, wenn gewünscht.

Wann ist der Einsatz sinnvoll?
Je nach Anwendungsfall, Datensatz-Anzahl und verarbeitender Programmiersprache kann sich durchaus ein Performance-Vorteil ergeben, wenn man MySQL die Arbeit erledigen lässt, die ansonsten innerhalb der Programmiersprache zu bewältigen wäre. Durch solche Funktionen kann man Daten aus unterschiedlichen Tabellen in gleicher Art und Weise ausgeben lassen und vereinheitlichen. Trotz der ursprünglich unterschiedlichen Strukturierung der Daten kann ein gemeinsamer Code zur Anzeige genutzt werden kann. Die Daten müssen nicht weiter bearbeitet werden. Nicht nur nützlich, sondern sogar unverzichtbar sind solche und ähnliche Funktionen immer dort, wo Daten-Bearbeitungen ohne Zwischenschritte vorgenommen werden sollen. Wollte man beispielsweise in allen Datensätzen, deren Firmen-Feld nicht leer ist, dem Firmennamen ein " GmbH" nachstellen, wäre dies mit "CONCAT()" problemlos möglich: "UPDATE kontakte SET firma=CONCAT(firma, ' GmbH') WHERE LENGTH(firma)".

Der einzige echte Nachteil der Nutzung von RDBMS-spezifischen Funktionen liegt in der Portabilität. Möchte man bei einem fertigen Projekt die SQL-Engine wechseln (was bei Nutzung von Datenbank-Abstraktions-Layern wie dem DBI in Perl oder der PHPLIB in PHP recht einfach ist), müssen die SQL-Statements gegebenenfalls angepasst werden. Da das aber nicht allzu oft vorkommt, kann man dieses Argument in der Regel getrost vergessen. (cb)

 

Interesante Links