SQL-Datenbanken effizient nutzen

Werbung

von Carsten Blüm

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.

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 zum Beispiel 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.

Weitere Beiträge:

Über Gastautor

DrWeb.de ist die "Grande Dame" des deutschen Bloggings und seit nunmehr 14 Jahren im Internet aktiv. Das beliebte Magazin richtet sich dabei an Webworker, Selbstständige, IT-Entscheider, Seitenbetreiber sowie Marketing-Verantwortliche und bietet einen Überblick im undurchdringlichen Dschungel zahlreicher "Geld verdienen im Internet" Konzepte. Werden Sie jetzt Gastautor und profitieren Sie von der großen Reichweite und den Markennamen DrWeb.de.

,

Ein Kommentar zu SQL-Datenbanken effizient nutzen

  1. Tino Bellmann 26. Juli 2010 at 16:45 #

    Ich habe mir in den letzten 5 Jahren ein eigenes CMS in PERL und MySQL geschrieben, welches auch in der Praxis ganz gut läuft. Nun hatte ich den Gedanken, dass Perl doch wohl ziemlich langsam wäre und ein wenig Tuning bräuchte. Man schleppt ja mit der Zeit immer mehr Code ein, der mit der heißen Nadel gestrickt ist und nach dem Motto “Hauptsache es funktioniert !” gebaut wird. Das wollte ich ändern – Also habe ich begonnen, an verschiedenen Punkten des Programmes die Laufzeit zu messen und auszuwerten.

    Beim einfachen Durchlauf mit etwa 1700 Programmzeilen benötigt das Programm einschließlich aller Selects und Updates sowie Ausgabe des erzeugten Contents an den Browser rund 50 Millisekunden.

    Leider benötigt dbi->connect an den localhost etwa 30 ms – auf einem 4600er X2 – und ich finde nirgendwo eine Lösung, wie man das tunen könnte. Ich verwende dabei die ganz normale, auch im CPAN bei DBI demonstrierte Lösung ohne Schnörkel und Spielchen.

    Auf einem alten Athlon 2100-Server braucht das ganze Programm knapp 300 ms und 2/3 dieser Zeit gehen für den Connect drauf…

    Hat jemand eine Idee ?

Hinterlasse eine Antwort

Bitte bei weiteren Kommentaren per Email benarichtigen! Auch möglich: Abo ohne Kommentar.

Spam protection by WP Captcha-Free