Die Grundlage unseres digitalen Uni­ver­sums ist ein stetig wach­sen­der Datenberg. Zentrales Element des Internets und der ver­netz­ten Welt sind Datenbank-Ma­nage­ment-Systeme wie MySQL. Diese er­mög­li­chen es, große Da­ten­men­gen elek­tro­nisch zu ver­ar­bei­ten, wi­der­spruchs­frei abzulegen und dauerhaft auf­zu­be­wah­ren. Dabei werden komplexe Da­ten­be­stän­de in handliche Teil­men­gen zerlegt und bei Bedarf in Relation gesetzt. In unserem MySQL-Tutorial für Anfänger führen wir Sie in die Grund­la­gen der Da­ten­bank­ver­wal­tung ein und zeigen Ihnen an Bei­spie­len, wie Sie die Da­ten­hal­tung für Ihr Web­pro­jekt mit MySQL op­ti­mie­ren.

Was ist MySQL?

MySQL gehört neben Oracle und Microsoft SQL Server zu den weltweit po­pu­lärs­ten re­la­tio­na­len Datenbank-Ma­nage­ment-Systemen (eine aktuelle Übersicht finden Sie auf db-engines.com). Die 1994 vom schwe­di­schen Un­ter­neh­men MySQL AB ent­wi­ckel­te Software steht heute unter der Schirm­herr­schaft der Oracle Cor­po­ra­ti­on und wird auf Basis eines dualen Li­zenz­sys­tems ver­trie­ben: Neben der pro­prie­tä­ren En­ter­pri­se-Aus­füh­rung bietet Oracle eine GPL-li­zen­zier­te, quell­of­fe­ne Version an. Diese Dop­pel­li­zen­zie­rung gibt Un­ter­neh­men die Mög­lich­keit, eigene An­wen­dun­gen auf Basis von MySQL zu ent­wi­ckeln, ohne diese der Open-Source-Lizenz un­ter­wer­fen zu müssen. In der Open-Source-Gemeinde stößt die Übernahme von MySQL durch Oracle jedoch über­wie­gend auf Kritik.

Tipp

Datenbank Hosting gesucht? Hier finden Sie pro­fes­sio­nel­les SQL-Hosting mit dem Datenbank-Ma­nage­ment-System Ihrer Wahl.

MySQL ist in C und C++ ge­schrie­ben und verfügt über einen Yacc-basierten SQL-Parser mit selbst ent­wi­ckel­tem Tokenizer (le­xi­ka­li­scher Scanner). Das Datenbank-Ma­nage­ment-System zeichnet sich zudem durch eine breite Be­triebs­sys­tem-Un­ter­stüt­zung aus.  

Fakt

Die Abkürzung SQL steht für „Struc­tu­red Query Language“, eine Com­pu­ter­spra­che, die der Ver­wal­tung von Da­ten­bank­struk­tu­ren dient. Mögliche Ope­ra­tio­nen umfassen das Abfragen, Einfügen, Ak­tua­li­sie­ren und Löschen von Da­ten­be­stän­den.

MariaDB – MySQL-Fork mit Potenzial

Die Ein­glie­de­rung des MySQL-Projekts in das Oracle-Pro­dukt­port­fo­lio stößt in der Ent­wick­ler­sze­ne über­wie­gend auf Miss­trau­en und Kritik. Dies hat in erster Linie damit zu tun, dass die Un­ter­schie­de zwischen der GPL-li­zen­zier­ten MySQL-Version und dem kos­ten­pflich­ti­gen En­ter­pri­se-Produkt stetig zunehmen. Neue Funk­tio­nen des Datenbank-Ma­nage­ment-Systems stehen immer häufiger nur in der pro­prie­tä­ren Version zur Verfügung. Nicht­öf­fent­li­che Fehler-Da­ten­ban­ken und mangelnde Tests lassen die Community vermuten, dass das Open-Source-Projekt unter den Fittichen des Software-Goliaths Oracle nur stief­müt­ter­lich behandelt wird. Der Rückhalt in der Open-Source-Ge­mein­schaft nimmt daher stetig ab.

Bereits 2009 hat das Kern­ent­wick­ler­team um den MySQL-Erfinder Michael „Monty“ Widenius dem beliebten Da­ten­bank­sys­tem den Rücken gekehrt und mit MariaDB einen quell­of­fe­nen Fork der Software initiiert. Ende 2012 leiteten mit Fedora, OpenSUSE, Slackware und Arch Linux die ersten Linux-Dis­tri­bu­tio­nen die Um­stel­lung von MySQL auf MariaDB als Standard-In­stal­la­ti­on ein. Zahl­rei­che Open-Source-Projekte sowie namhafte Software-Firmen und Web­platt­for­men folgten dem Beispiel – darunter Mozilla, Ubuntu, Google, Red Hat En­ter­pri­se Linux, Web of Trust, Team Speak, die Wikimedia Foun­da­ti­on und das bereits an­ge­spro­che­ne Software-Projekt XAMPP.

Schon jetzt zeichnet sich ab, dass sich MariaDB im Vergleich zur quell­of­fe­nen MySQL-Version kon­ti­nu­ier­li­cher wei­ter­ent­wi­ckelt. Es ist daher an­zu­neh­men, dass der Fork seinem Mut­ter­pro­jekt bald den Rang ablaufen wird.

Tipp

Als Fork wird in der Software-Branche ein Ent­wick­lungs­zweig be­zeich­net, der durch Ab­spal­tung von einem (meist quell­of­fe­nen) Projekt entsteht. Ein Fork baut auf dem Quellcode des Mut­ter­pro­jekts auf und ent­wi­ckelt diesen in einem un­ab­hän­gi­gen Fol­ge­pro­jekt weiter.

Da­ten­bank­sys­te­me

Die elek­tro­ni­sche Da­ten­ver­wal­tung findet heute größ­ten­teils in Da­ten­bank­sys­te­men (DBS) statt. Diese bestehen grund­sätz­lich aus zwei Kom­po­nen­ten: der Datenbank (DB) selbst und dem zu ihrer Ver­wal­tung nötigen Datenbank-Ma­nage­ment-System (DBMS).

  • Das Datenbank-Ma­nage­ment-System: Beim DBMS handelt es sich um eine Software wie MySQL, die der Ver­wal­tung des DBS dient. Zu den Aufgaben dieser Ver­wal­tungs­soft­ware gehört die Struk­tu­rie­rung der Daten gemäß einem vor­de­fi­nier­ten Da­ten­bank­mo­dell. Darüber hinaus kon­trol­liert das DBMS Schreib- und Le­se­zu­grif­fe auf die Datenbank, verwaltet große Da­ten­men­gen sowie parallele Da­ten­bank­zu­grif­fe und sorgt dafür, dass Richt­li­ni­en zu Da­ten­in­te­gri­tät, Da­ten­schutz und Da­ten­si­cher­heit ein­ge­hal­ten werden.
  • Die Datenbank: Eine Datenbank ist ein in­halt­lich zu­sam­men­hän­gen­der Da­ten­be­stand, z. B. Kun­den­da­ten oder die CMS-Daten. Ein DBMS kann mehrere DBs zugleich verwalten.

Folgende Grafik zeigt eine sche­ma­ti­sche Dar­stel­lung eines Da­ten­bank­sys­tems:

Das re­la­tio­na­le Da­ten­bank­mo­dell

MySQL ist der De­fi­ni­ti­on nach ein re­la­tio­na­les DBMS. Das bedeutet, dass die Da­ten­ver­wal­tung auf einem ta­bel­len­ba­sier­ten Da­ten­bank­mo­dell beruht. Sämtliche Daten, die MySQL ver­ar­bei­tet, werden in Tabellen ge­spei­chert, die sich durch Schlüssel (keys) in Beziehung setzen lassen.

Wir ver­an­schau­li­chen dies an einem einfachen Beispiel. Nach­ste­hend finde Sie die beiden Tabellen autoren und werke:

Alle Tabellen einer re­la­tio­na­len Datenbank bestehen aus Spalten und Zeilen. Jede Spalte einer Tabelle steht für ein be­stimm­tes Attribut. In der Tabelle autoren finden sich bei­spiels­wei­se die Attribute id, vorname und nachname. Die Zeilen einer Tabelle werden als Rows be­zeich­net und be­inhal­ten je einen Datensatz. Dieser wird in der Regel durch einen Pri­mär­schlüs­sel eindeutig iden­ti­fi­ziert (num­me­riert). Welches Attribut als Pri­marschlüs­sel fungiert, wird bei der Er­stel­lung der Tabelle definiert. Vor­aus­set­zung ist, dass der Pri­mär­schlüs­sel eine ein­deu­ti­ge Zuordnung er­mög­licht. Dazu darf jeder Pri­mär­schlüs­sel innerhalb einer Spalte nur einmal verwendet werden. Es empfiehlt sich eine Durch­num­me­rie­rung via ID.

Die Tabelle werke führt neben dem Pri­mär­schlüs­sel werke_id zudem die autoren_id als Fremd­schlüs­sel (foreign key) auf. Dieser stellt eine Beziehung zwischen beiden Tabellen her und er­mög­licht es, die Da­ten­sät­ze der einen Tabelle mit denen der anderen zu ver­knüp­fen. Setzt man zwei Tabellen einer re­la­tio­na­len Datenbank in Ver­bin­dung, spricht man von einem Join. Ein solcher lässt sich bei­spiels­wei­se durch folgende Da­ten­bank­ab­fra­ge rea­li­sie­ren: „Rufe alle Werke des Autors John Ronald Reuel Tolkien inklusive Datum der Erst­ver­öf­fent­li­chung ab“.

Tolkien ist in der Tabelle autoren mit dem Pri­mär­schlüs­sel autoren_id 1 gelistet. Um sämtliche Werke des Autors ab­zu­fra­gen, kommt dieser in der Tabelle werke als Fremd­schlüs­sel zum Einsatz. Es werden somit alle Rows abgerufen, die mit der autoren_id 1 verknüpft sind.

In der Praxis werden MySQL-Da­ten­bank­ope­ra­tio­nen mit stan­dar­di­sier­ten SQL-Befehlen wie SELECT, INSERT, UPDATE und DELETE rea­li­siert. Auf diese werden wir in den folgenden Kapiteln unseres MySQL-Tutorial noch näher eingehen.

Natürlich hätte man sämtliche Daten zu beiden Autoren und deren Werken in nur einer Tabelle speichern können. Eine solche Da­ten­hal­tung führt jedoch dazu, dass eine Datenbank eine Vielzahl red­un­dan­ter Einträge be­inhal­tet, da bei­spiels­wei­se die Daten zu den Spalten vorname und nachname in diesem Fall für jedes Werk einzeln auf­ge­führt werden müssten. Eine solche Redundanz belastet nicht nur den Speicher, sondern führt auch dazu, dass etwaige Ak­tua­li­sie­run­gen an mehreren Stellen der Datenbank vor­ge­nom­men werden müssen. Bei der Arbeit mit re­la­tio­na­len Da­ten­bank­sys­te­men be­schränkt man sich daher darauf, nur einen Sach­ver­halt pro Tabelle ab­zu­bil­den. Man spricht in diesem Fall von einer Nor­ma­li­sie­rung der Daten.

Das zentrale An­wen­dungs­feld von MySQL ist die Da­ten­spei­che­rung im Rahmen dy­na­mi­scher Web­an­ge­bo­te. Die Kom­bi­na­ti­on von MySQL mit der Webserver-Software Apache und den Skript­spra­chen PHP oder Perl hat sich als klas­si­sches Software-Gerüst in der Web­ent­wick­lung etabliert. Der Web-Stack lässt sich als LAMP (Linux), MAMP (macOS) oder WAMP (Windows) mit allen gängigen Server-Be­triebs­sys­te­men rea­li­sie­ren.

MySQL-Ein­stei­gern empfehlen wir die lokale Text­um­ge­bung XAMPP, um erste Er­fah­run­gen mit dem Datenbank-Ver­wal­tungs­sys­tem zu sammeln. Dieses setzt in der aktuellen Version auf MariaDB.

In­stal­la­ti­on des Datenbank-Ma­nage­ment-Systems

Um Ihnen die MySQL-Grund­la­gen näher zu bringen, werden wir mit pra­xis­na­hen Bei­spie­len arbeiten. Unserer MySQL-Ein­füh­rung liegt dabei die Test­um­ge­bung XAMPP zugrunde. Code-Snippets und Screen­shots kon­zen­trie­ren sich auf Da­ten­bank­ope­ra­tio­nen, die via PHP mithilfe eines Apache HTTP Servers lokal auf einem Windows-Rechner rea­li­siert wurden. Statt der klas­si­schen MySQL-Datenbank kommt dabei der Fork MariaDB zum Einsatz. Derzeit sind beide Datenbank-Ma­nage­ment-Systeme so kom­pa­ti­bel, dass sich alle Ope­ra­tio­nen 1:1 über­tra­gen lassen. Für Sie als End­an­wen­der macht es im Rahmen eines Ein­stei­ger-Tutorials keinen Un­ter­schied, ob Sie mit MySQL oder MariaDB arbeiten. Wie Sie eine solche Test­um­ge­bung lokal auf Ihrem Windows-Rechner in­stal­lie­ren, ist Thema unseres XAMPP-Tutorials. Möchten Sie den Umgang mit re­la­tio­na­len Da­ten­ban­ken von der Pike auf lernen, empfehlen wir, direkt mit MariaDB ein­zu­stei­gen. Eine al­ter­na­ti­ve Test­um­ge­bung auf Basis von MySQL steht Ihnen mit AMPPS jedoch ebenfalls kostenlos zur Verfügung. Darüber hinaus besteht die Mög­lich­keit, einen be­nut­zer­de­fi­nier­ten Web-Stack auf­zu­set­zen. MySQL und MariaDB lassen sich bei Bedarf mit diversen Be­triebs­sys­te­men, Web­ser­vern und Skript­spra­chen kom­bi­nie­ren. Kos­ten­lo­se, GPL-li­zen­zier­te Down­load­pa­ke­te werden unter mysql.de und mariadb.com angeboten. De­tail­lier­te In­stal­la­ti­ons­an­lei­tun­gen für ver­schie­de­ne Platt­for­men finden Sie in den eng­lisch­spra­chi­gen Do­ku­men­ta­tio­nen von MySQL und MariaDB.

Da­ten­bank­ver­wal­tung mit phpMy­Ad­min

Bei der Ver­wal­tung von MySQL stützen wir uns auf die freie Web­an­wen­dung phpMy­Ad­min. Diese ist bereits im In­stal­la­ti­ons­um­fang des XAMPP-Software-Bundles enthalten, wird auf der of­fi­zi­el­len Projekt-Website jedoch auch separat als Download-Paket angeboten. phpMy­Ad­min gilt als Stan­dard­soft­ware zur Ad­mi­nis­tra­ti­on von MySQL-Da­ten­ban­ken im World Wide Web. Die in PHP und Ja­va­Script ge­schrie­be­ne Web­an­wen­dung er­mög­licht es, Da­ten­bank­ope­ra­tio­nen über eine grafische Be­nut­zer­ober­flä­che durch­zu­füh­ren. So erstellen und verwalten Sie die Tabellen Ihrer re­la­tio­na­len Datenbank bequem per Klick im Web­brow­ser. Die Kenntnis ent­spre­chen­der SQL-Befehle ist dazu zunächst nicht notwendig.

phpMy­Ad­min aufrufen

Wurde das Software-Bundle XAMPP in­stal­liert, starten Sie das Datenbank-Ma­nage­ment-System (MySQL oder MariaDB) analog zu den anderen Kom­po­nen­ten des Test-Stacks über das Control-Panel. Nutzen Sie dazu die unter „Actions“ auf­ge­führ­te Schalt­flä­che „Start“. Um phpMy­Ad­min über den Web­brow­ser aufrufen zu können, müssen Sie zudem den Webserver Apache starten. Ak­ti­vier­te Module werden im XAMPP-Control-Panel grün hin­ter­legt. Zu­sätz­lich bekommen Sie den aktuellen Status der XAMPP-Module als Meldung im Text­fens­ter aus­ge­ge­ben.

Fakt

XAMPP wurde im Rahmen des Software-Projekts Apache Friends als kompaktes Test­sys­tem für den Einsatz auf einem lokalen Rechner ent­wi­ckelt. Das Software-Bundle ist nicht dazu gedacht, Web­diens­te im Internet be­reit­zu­stel­len. Als Pro­duk­tiv­sys­tem ist XAMPP aufgrund zahl­rei­cher Ein­schrän­kun­gen im Bereich der Si­cher­heit nicht geeignet.

Im lokalen Test­be­trieb steht Ihnen die Web­ober­flä­che der Ad­mi­nis­tra­ti­ons­soft­ware unter "http://localhost/phpmy­ad­min/" zur Verfügung.

Sofern Sie bei der MySQL-In­stal­la­ti­on ein Passwort für den root-Account definiert haben, fragt phpMy­Ad­min dieses über einen Log-in-Bild­schirm ab. Nutzen Sie phpMy­Ad­min im Rahmen eines Web­hos­ting-Produkts, werden ent­spre­chen­de An­mel­de­da­ten über den je­wei­li­gen Anbieter vergeben. In der Regel besitzen Sie in diesem Fall keine root-Rechte.

Nach er­folg­rei­chem Log-in prä­sen­tiert phpMy­Ad­min Ihnen die Start­sei­te der Anwendung. Diese bietet Ihnen die Mög­lich­keit, grund­le­gen­de Ein­stel­lun­gen zum Zei­chen­satz (Kollation) der MySQL-Ver­bin­dung zu treffen sowie den ge­wünsch­ten An­zei­ge­mo­dus (Sprache, Design und Schrift­grö­ße) zu wählen. Zudem finden Sie auf der rechten Seite eine Übersicht der Eckdaten Ihres Datenbank-Servers, der ver­wen­de­ten Webserver-Software sowie In­for­ma­tio­nen über die aktuelle Version von phpMy­Ad­min. Die Me­nü­leis­te der Start­sei­te ist wie alle anderen Me­nü­leis­ten der Anwendung in Form von Re­gis­ter­kar­ten gestaltet. Zur Auswahl stehen die Tabs Databases, SQL, Status, User accounts, Export, Import, Settings, Re­pli­ca­ti­on, Variables und More.

Am linken Rand der Be­nut­zer­ober­flä­che finden Sie ein Na­vi­ga­ti­ons-Panel. Hier werden alle Tabellen auf­ge­führt, auf die Sie mithilfe von phpMy­Ad­min in Ihrer Datenbank zugreifen können. Unter dem Programm-Logo in der linken oberen Ecke hält die Software Links zur Start­sei­te sowie zu of­fi­zi­el­len Do­ku­men­ta­tio­nen bereit. Darüber hinaus haben Sie die Mög­lich­keit, das Na­vi­ga­ti­ons-Panel zu kon­fi­gu­rie­ren und Ihre Ansicht zu ak­tua­li­sie­ren.

Wir starten unseren MySQL-Crashkurs damit, dass Sie Ihre erste Datenbank anlegen.

Datenbank anlegen

Um eine Datenbank mit phpMy­Ad­min anzulegen, wählen zunächst den Tab „Databases“ (Da­ten­ban­ken) in der Me­nü­leis­te der Start­sei­te aus.

Geben Sie einen ge­wünsch­ten Namen für Ihre Datenbank in das Ein­ga­be­feld unter „Create database“ (Neue Datenbank anlegen) ein und wählen Sie einen Zei­chen­satz. Wir empfehlen die Kollation utf8mb4_unicode_ci. Mit der Wahl eines Zei­chen­sat­zes teilen Sie dem Da­ten­bank­ser­ver mit, welche Codierung für die zu sendenden bzw. emp­fan­gen­den Daten verwendet werden soll. Die mb4-Varianten erlauben auch exotische Zeichen wie Symbole oder Emojis, die außerhalb der Unicode-Basis-Ebene (Basic Mul­ti­l­in­gu­al Plane) liegen, und sind deshalb zu empfehlen.

Be­stä­ti­gen Sie Ihre Eingabe mit einem Klick auf „Create“ (Anlegen). Die von Ihnen erstellte Datenbank wird im Na­vi­ga­ti­ons-Panel auf der linken Bild­schirm­sei­te angezeigt. Neu erstellte Da­ten­ban­ken enthalten zunächst keine Inhalte. Um Daten zu hin­ter­le­gen, erstellen Sie im nächsten Schritt eine Tabelle.

Tabellen anlegen

Um eine neue Tabelle anzulegen, wählen Sie die ge­wünsch­te Datenbank aus und na­vi­gie­ren über die Me­nü­leis­te in den Tab „Structure“ (Struktur).

Legen Sie eine Tabelle an, indem Sie einen Namen (z. B. users) sowie die ge­wünsch­te Anzahl an Spalten in die Schalt­flä­che „Create table“ (Erzeuge Tabelle) eingeben. Sie erinnern sich: Jede Spalte steht für ein Attribut eines Da­ten­sat­zes. Benötigen Sie zu­sätz­li­che Spalten, können Sie diese auch nach­träg­lich hin­zu­fü­gen.

Möchten Sie bei­spiels­wei­se eine Nut­zer­da­ten­bank für Ihre Website anlegen, bieten sich folgende Be­zeich­nun­gen für die Spalten der Tabelle an:

Für Ihre Nut­zer­da­ten­bank erstellen Sie somit eine Tabelle users mit sieben Spalten. Ihre Eingabe be­stä­ti­gen Sie mit „Go“.

Nachdem die Tabelle angelegt wurde, gibt phpMy­Ad­min Ihnen die Mög­lich­keit, Be­zeich­nun­gen für die Ta­bel­len­spal­ten zu de­fi­nie­ren und For­matein­stel­lun­gen für die zu er­war­ten­den Daten zu treffen.

Eine Be­schrei­bung der Ta­bel­len­struk­tur und möglicher For­ma­tie­run­gen finden Sie in folgender Tabelle.

Option Be­schrei­bung
Name Jede Spalte einer Da­ten­bank­ta­bel­le wird mit einem Namen versehen. Dieser kann mit Ein­schrän­kun­gen frei gewählt werden. Un­pro­ble­ma­tisch sind la­tei­ni­sche Buch­sta­ben (groß und klein, aber ohne Umlaute), Ziffern, Dol­lar­zei­chen und Un­ter­strich. Diesen können Sie als Al­ter­na­ti­ve zum nicht erlaubten Leer­zei­chen verwenden (falsch: user id; richtig: user_id). Spal­ten­na­men dürfen nicht nur aus Ziffern bestehen. Zudem finden sich in der Da­ten­bank­spra­che SQL diverse Keywords, die für bestimme Aufgaben re­ser­viert sind. Eine Liste lässt sich der MySQL-Do­ku­men­ta­ti­on entnehmen. Die meisten dieser Ein­schrän­kun­gen dürfen Sie umgehen, müssen dann aber die jeweilige Spalte immer in Backticks (``) setzen. Die gleichen Regeln gelten übrigens auch für Tabellen- und andere Namen in MySQL. Zu empfehlen sind spre­chen­de Spal­ten­na­men passend zum je­wei­li­gen Attribut.
Type Der Datentyp gibt an, welche Art von Daten in einer Spalte ge­spei­chert wird. MySQL und MariaDB er­mög­li­chen Ihnen, Daten als Ganz- und Fließ­kom­ma­zah­len, Zeit- und Da­tums­an­ga­ben sowie Text­strings und Bi­när­da­ten zu de­fi­nie­ren. Eine Be­schrei­bung finden Sie in der Tabelle Da­ten­ty­pen.
Length/Values Bei manchen Da­ten­ty­pen (zum Beispiel Text­strings) können Sie den Werten einer Spalte eine Ma­xi­mal­län­ge zuweisen. Diese For­ma­tie­rung ist optional.
Default Die Option „Default“ erlaubt Ihnen, einen Stan­dard­wert für eine Spalte zu de­fi­nie­ren. Dieser wird immer dann au­to­ma­tisch eingefügt, wenn ein Datensatz keinen Wert für die jeweilige Spalte enthält.
Collation Mit der Option „Collation“ de­fi­nie­ren Sie für eine Spalte einen be­stimm­ten Zei­chen­typ, der von den globalen Da­ten­bank­ein­stel­lun­gen abweichen kann. Sie können die Kodierung auch auf Ta­bel­len­ebe­ne für alle Spalten ändern.
At­tri­bu­tes Manche Da­ten­ty­pen lassen sich durch optionale Attribute näher bestimmen. So können Sie bei­spiels­wei­se mit den At­tri­bu­ten signed und unsigned festlegen, ob eine Ganz- oder Fließ­kom­ma­zahl auch negative (signed) oder nur positive (unsigned) Werte annehmen kann.
Index Über die Option „Index“ de­fi­nie­ren Sie Regeln für die In­de­xie­rung. Wählen Sie für eine Spalte die Index-Ein­stel­lung PRIMARY, gilt diese als Pri­mär­schlüs­sel der Tabelle. Die Ein­stel­lung UNIQUE legt fest, dass Werte innerhalb dieser Spalte nur einmal ge­spei­chert werden können. So lassen sich Dop­pe­lun­gen bei Bedarf vermeiden.
A_I Die Abkürzung „A_I“ steht für AUTO_INCREMENT und weist das Datenbank-Ma­nage­ment-System an, einen Wert au­to­ma­tisch hoch­zu­zäh­len, wenn beim Anlegen eines Da­ten­sat­zes keiner angegeben wurde. Zum Einsatz kommt diese Option bei der In­de­xie­rung von Da­ten­sät­zen.
Comments Das Feld „Comments“ er­mög­licht Ihnen, Ta­bel­len­spal­ten mit Kom­men­ta­ren zu versehen.

Die hier dar­ge­stell­ten Optionen umfassen die wich­tigs­ten Ein­stel­lun­gen zur For­ma­tie­rung von Ta­bel­len­spal­ten. Wenn Sie mithilfe der Bild­lauf­leis­te weiter nach rechts scrollen, finden Sie weitere Ein­stel­lungs­mög­lich­kei­ten, die wir in diesem MySQL-Tutorial für Ein­stei­ger nicht an­spre­chen.

Nach­ste­hen­de Tabelle führt ver­schie­de­ne Da­ten­ty­pen, die sich mit MySQL und MariaDB ver­ar­bei­ten lassen, sowie deren Wer­te­be­reich und Spei­cher­be­darf auf.

Typ Be­schrei­bung Wer­te­be­reich Spei­cher­be­darf
TINYINT Eine sehr kleine Ganzzahl Ohne Vor­zei­chen: 0 bis 255 Mit Vor­zei­chen: -128 bis +127 1 Byte
SMALLINT Eine kleine Ganzzahl Ohne Vor­zei­chen: 0 bis 65.535 Mit Vor­zei­chen: -32.768 bis +32.767 2 Byte
MEDIUMINT Eine Ganzzahl mittlerer Größe Ohne Vor­zei­chen: 0 bis 16.777.215 Mit Vor­zei­chen: -8.388.608 bis +8.388.607 3 Byte
INT/INTEGER Eine Ganzzahl normaler Größe Ohne Vor­zei­chen: 0 bis 4.294.967.295 Mit Vor­zei­chen: -2.147.483.648 bis +2.147.483.647 4 Byte
BIGINT Eine große Ganzzahl Ohne Vor­zei­chen: 0 bis 18.446.744.073.709.551.615 Mit Vor­zei­chen: -9.223.372.036.854.775.808 bis +9.223.372.036.854.775.807 8 Byte
FLOAT Eine Fließ­kom­ma­zahl mit einfacher Ge­nau­ig­keit Ohne Vor­zei­chen: 0 bis 3,4e+38 Mit Vor­zei­chen: -3,4e+38 bis 3,4e+38 4 Byte
DOUBLE Eine Fließ­kom­ma­zahl mit doppelter Ge­nau­ig­keit Ohne Vor­zei­chen: 0 bis 3,4e+38 Mit Vor­zei­chen: -3,4e+38 bis 3,4e+38 8 Byte
DATE Datum im Format 'YYYY-MM-DD' '1000-01-01' bis '9999-12-31' 3 Byte
TIME Zeit­an­ga­be im Format 'HH:MM:SS.ssssss' '-838:59:59.999999' bis '838:59:59.999999' 3 Byte
DATETIME Da­tums­an­ga­be im Format 'YYYY-MM-DD HH:MM:SS.ssssss' Ent­spricht DATE und TIME (bis 23:59:59.999999 Stunden) 8 Byte
TIMESTAMP Zeit­stem­pel im Format 'YYYY-MM-DD HH:MM:DD' '1970-01-01 00:00:01' (UTC) bis '2038-01-19 05:14:07' (UTC) 4 Byte
YEAR Jahr zwischen 1901 bis 2155 1901 bis 2155 und 0000 1 Byte
CHAR Zei­chen­ket­te fester Länge; Anzahl der Zeichen ent­spricht M Für M: 0 bis 255 Zeichen M Byte
VARCHAR Zei­chen­ket­te variabler Länge; Anzahl der Zeichen ent­spricht M Für M: 0 bis 65.535 Zeichen Max. M + 2 Byte
TINYTEXT Sehr kleine Zei­chen­ket­te variabler Länge; Anzahl der Zeichen ent­spricht M Für M: 0 bis 255 Zeichen M + 1 Byte
TEXT Zei­chen­ket­te variabler Länge; Anzahl der Zeichen ent­spricht M Für M: 0 bis 65.535 Zeichen M + 2 Byte
ME­DI­UM­TEXT Mit­tel­gro­ße Zei­chen­ket­te variabler Länge; Anzahl der Zeichen ent­spricht M Für M: 0 bis 16.777.215 Zeichen M + 3 Byte
LONGTEXT Lange Zei­chen­ket­te variabler Länge; Anzahl der Zeichen ent­spricht M Für M: 0 bis 4.294.967.295 Zeichen (4 GB) M + 4 Byte
BLOB Bei einem BLOB (Binary Large Object) handelt es sich um ein binäres Objekt mit Daten variabler Länge (z. B. Bilder, Au­dio­da­ten). Max. Länge M: 65.535 Byte M + 2 Byte
TINYBLOB Kleines binäres Objekt mit Daten variabler Länge Max. Länge M: 255 Byte M + 1 Byte
ME­DI­UM­B­LOB Mittleres binäres Objekt mit Daten variabler Länge. Max. Länge M: 16.777.215 Byte M + 3 Byte
LONGBLOB Großes binäres Objekt mit Daten variabler Länge. Max. Länge M: 4.294.967.295 Byte (4 GB). M + 4 Byte
ENUM (Auf­zäh­lung) Ein String-Objekt, dessen zulässige Werte bei der Er­stel­lung der Spalte definiert werden Max. 65,535 un­ter­schied­li­che Elemente 1 oder 2 Bytes, abhängig von der Anzahl der möglichen Werte
SET Ein String-Objekt, dessen zulässige Werte bei der Ta­bel­len­er­stel­lung definiert werden. Mehr­fach­aus­wahl ist möglich Max. 64 un­ter­schied­li­che Werte 1, 2, 3, 4, oder 8, abhängig von der Anzahl der möglichen Werte

Für die Bei­spiel­ta­bel­le users haben wir folgende Ein­stel­lun­gen getroffen:

Mögliche Werte für die Spalte id wurden als Ganzzahl (Integer, INT) definiert und tragen das Attribut UNSIGNED. Die id kann somit lediglich positive Zah­len­wer­te annehmen. Unter „Index“ haben wir für die id die Ein­stel­lung PRIMARY aus­ge­wählt. Die Iden­ti­fi­ka­ti­ons­num­mer fungiert somit als Pri­mär­schlüs­sel für die Tabelle users. Das Häkchen bei „A_I“ (Auto_Increment) si­gna­li­siert dem Datenbank-Ma­nage­ment-System, dass IDs für jeden Eintrag als fort­lau­fen­de Nummern au­to­ma­tisch erzeugt werden sollen.

Die Werte für die Spalten forename, surname, email und password wurden als Datentyp VARCHAR definiert. Es handelt sich somit um variable Zei­chen­ket­ten, deren Länge (M) wir mit der Option „Length/Value“ auf 50 Zeichen ein­ge­schränkt haben. Für die Spalte email wurde zudem die Index-Option UNIQUE aktiviert. So stellen wir sicher, dass jede E-Mail-Adresse in unserer Tabelle nur einmal ge­spei­chert wird.

Für die Spalten created_at und updated_at haben wir den Datentyp TIMESTAMP gewählt. Das Datenbank-Ma­nage­ment-System speichert Zeitdaten zur Er­stel­lung und Ak­tua­li­sie­rung von Da­ten­sät­zen somit im Format YYYY-MM-DD HH:MM:DD. Da das System für jeden neuen Eintrag au­to­ma­tisch einen Zeit­stem­pel erstellen soll, wählen wir für die Spalte created_at den Stan­dard­wert CURRENT_TIMESTAMP. Die Spalte updated_at wird erst dann relevant, wenn wir einen Eintrag ak­tua­li­sie­ren. Wir erlauben daher Null-Werte für diese Spalte und setzten NULL als Stan­dard­wert.

Fakt

Der Wert NULL re­prä­sen­tiert in PHP einen leeren Eintrag. Ein Datenfeld hat den Wert NULL, wenn diesem noch kein Wert zu­ge­wie­sen wurde.

Als Storage-Engine verwenden wir das MySQL-Standard-Ta­bel­len­for­mat InnoDB.

Alle Ta­bel­len­ein­stel­lun­gen, die Sie über die grafische Be­nut­zer­ober­flä­che treffen, werden von phpMy­Ad­min in SQL-Code übersetzt. Diesen lassen Sie sich bei Bedarf über die Schalt­flä­che „SQL-Vorschau“ ausgeben.

CREATE TABLE test.users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT , forename VARCHAR(50) NOT NULL , surname VARCHAR(50) NOT NULL , email VARCHAR(50) NOT NULL , password VARCHAR(50) NOT NULL , created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , updated_at TIMESTAMP NULL DEFAULT NULL , PRIMARY KEY (id), UNIQUE (email)) ENGINE = InnoDB;

Eine de­tail­lier­te Be­schrei­bung der SQL-Syntax folgt im Kapitel zu Da­ten­bank­ab­fra­gen.

Ein Klick auf „Save“ speichert Ihre Ein­stel­lun­gen. Die Tabelle users wird im Na­vi­ga­ti­ons-Panel unter der Datenbank test angezeigt.

Tabellen verwalten

Um eine erstellte Tabelle zu verwalten, klicken Sie auf den Ta­bel­len­na­men im Na­vi­ga­ti­ons-Panel. phpMy­Ad­min zeigt Ihnen im Tab „Browse“ (Anzeige) eine Übersicht der in der je­wei­li­gen Tabelle ge­spei­cher­ten Daten. Im Fall unserer Bei­spiel­ta­bel­le liefert die Abfrage ein leeres Resultat. Es befinden sich noch keine Da­ten­sät­ze in der Tabelle users.

Zur Ver­wal­tung von Da­ten­ta­bel­len stehen Ihnen in der Me­nü­leis­te ver­schie­de­ne Tabs zur Verfügung. Möchten Sie die Struktur einer Da­ten­ta­bel­le verändern, wählen Sie den Tab „Structure“. Neue Da­ten­sät­ze fügen Sie Ihrer Tabelle im Tab „Insert“ (Einfügen) hinzu. Darüber hinaus er­mög­licht es phpMy­Ad­min, Tabellen zu durch­su­chen, Be­rech­ti­gun­gen zu verwalten und Da­ten­sät­ze zu ex­por­tie­ren oder aus anderen Tabellen zu im­por­tie­ren.

Ta­bel­len­struk­tur über­ar­bei­ten

Möchten Sie Ihre Tabelle nach­träg­lich um zu­sätz­li­che Spalten ergänzen, exis­tie­ren­de löschen oder über­ar­bei­ten, wechseln Sie in den Reiter „Structure“.

Ergänzen Sie Spalten über die Schalt­flä­che „Add columns“, indem Sie die ge­wünsch­te Anzahl neuer Spalten sowie die jeweilige Position angeben.

In folgender Grafik wird die neue Spalte nach updated_at eingefügt:

Möchten Sie bereits be­stehen­de Spalten löschen, wählen Sie diese über ein Häkchen in der Check-Box aus und klicken an­schlie­ßend auf „Drop“.

Das Be­ar­bei­ten einer Spalte erfolgt über die Schalt­flä­che „Change“. Sie gelangen in eine Be­ar­bei­tungs­an­sicht, die der gleicht, die Sie bereits von der Ta­bel­len­er­stel­lung kennen:

Än­de­run­gen an der Ta­bel­len­struk­tur können unter Umständen zu Da­ten­ver­lust führen. Bevor sie bereits angelegte Ta­bel­len­spal­ten be­ar­bei­ten oder löschen, sollten Sie in jedem Fall ein Back-up Ihrer Datenbank anlegen. Wechseln Sie dazu in den Tab „Export“ (Ex­por­tie­ren), wählen Sie das ge­wünsch­te Da­ten­for­mat für das Back-up aus und be­stä­ti­gen Sie mit „Go“. Es öffnet sich ein Dia­log­fens­ter, in dem Ihr Web­brow­ser das Spei­cher­ziel für den Download abfragt. Eine Al­ter­na­ti­ve zum Datenbank-Back-up via phpMy­Ad­min stellt das kos­ten­lo­se Si­che­rungs­pro­gramm MyS­QLDum­per dar.

Ta­bel­len­ein­trä­ge erstellen

Um Ihre Tabelle via phpMy­Ad­min mit Daten zu befüllen, stehen Ihnen zwei Mög­lich­kei­ten offen: Entweder Sie im­por­tie­ren Da­ten­sät­ze aus einer externen Datei (z. B. aus einem Back-up) oder Sie erstellen Ta­bel­len­ein­trä­ge manuell. Wählen Sie dazu die Bei­spiel­ta­bel­le users aus und na­vi­gie­ren Sie in den Tab „Insert“ (Einfügen).

phpMy­Ad­min zeigt Ihnen folgende Da­ten­ein­ga­be­mas­ke:

Unter „Column“ sind die Spalten auf­ge­lis­tet, die wir für die Tabelle users definiert haben. Unter „Type“ finden Sie die In­for­ma­ti­on, welcher Datentyp für die jeweilige Spalte erwartet wird, sowie eine etwaige Zei­chen­be­gren­zung in Klammern. Den Bereich „Function“ klammern wir aus und gehen direkt zu „Value“ über. In diesem Bereich de­fi­nie­ren wir die Werte für die einzelnen Spalten unserer Bei­spiel­ta­bel­le.

Im vor­he­ri­gen Kapitel haben wir die Tabelle users so kon­fi­gu­riert, dass die Da­ten­fel­der für die Spalten id, created_at und updated_at vom Datenbank-Ma­nage­ment-System au­to­ma­tisch mit Inhalten versehen werden. In der Spalte id vergeben wir via AUTO_INCREMENT eine laufende Iden­ti­fi­zie­rungs­num­mer für jeden neuen Eintrag. Das Feld created_at wird au­to­ma­tisch mit dem aktuellen Zeit­stem­pel versehen. Und für updated_at vergibt das System zunächst den Stan­dard­wert NULL. Wir müssen somit lediglich die Daten für die Spalten forename, surname, email und password manuell eintragen. Wir ver­an­schau­li­chen dies an fiktiven Nut­zer­da­ten:

  • forename: John
  • surname: Doe
  • email: john@doe.com
  • password: qwertz

Mit einem Klick auf „Go“ über­tra­gen Sie die Daten in Ihre Tabelle. phpMy­Ad­min springt au­to­ma­tisch in den Tab „SQL“ und zeigt Ihnen die aus­ge­führ­te Da­ten­bank­ope­ra­ti­on als Statement gemäß der SQL-Syntax an:

INSERT INTO users (id, forename, surname, email, password, created_at, updated_at) VALUES (NULL, 'John', 'Doe', 'john@doe.com', 'qwertz', CURRENT_TIMESTAMP, NULL);

Prin­zi­pi­ell lassen sich alle Da­ten­bank­ope­ra­tio­nen, die Sie via phpMy­Ad­min kom­for­ta­bel über eine grafische Be­nut­zer­ober­flä­che durch­füh­ren, auch in der Da­ten­bank­spra­che SQL notieren. Dies gehört im Rahmen der Web­ent­wick­lung zum Stan­dard­vor­ge­hen.

So­ge­nann­te SQL-Queries (Abfragen) finden sich im Quellcode aller dy­na­mi­schen Web­an­wen­dun­gen und er­mög­li­chen dem Webserver die In­ter­ak­ti­on mit der Datenbank. Die Da­ten­bank­spra­che SQL stützt sich dabei auf Befehle – bei­spiels­wei­se, um Daten abzurufen und im Rahmen der Pro­gramm­aus­füh­rung zu nutzen. Die wich­tigs­ten SQL-Befehle SELECT, INSERT, DELETE und UPDATE sowie die Syntax der grund­le­gen­den Da­ten­bank­ope­ra­tio­nen the­ma­ti­sie­ren wir in den folgenden Kapiteln unsres MySQL-Tutorials.

Zunächst füllen wir unsere Tabelle users jedoch mit weiteren Nut­zer­da­ten und schauen uns die Ta­bel­len­über­sicht im Tab „Browse“ an:

Ein Klick auf den je­wei­li­gen Spal­ten­na­men sortiert die Tabelle in der ge­wünsch­ten Rei­hen­fol­ge.

Da­ten­bank­ver­bin­dung aufbauen

Nachdem wir unsere Be­spiel­ta­bel­le users mit Einträgen befüllt haben, werden wir uns in den nächsten Kapiteln anschauen, wie sich die hin­ter­leg­ten Daten via PHP über den Apache Webserver abfragen lassen.

Dazu stellen wir im ersten Schritt eine Da­ten­bank­ver­bin­dung her. In PHP stehen Ihnen dazu drei Schnitt­stel­len zur Verfügung: MySQL Extension, MySQL Improved Extension (MySQLi) und PHP Data Objects (PDO).

  • MySQL-Extension: Bei MySQL-Extension handelt es sich um eine überholte MySQL-Schnitt­stel­le, die früher sehr populär war, heute jedoch als veraltet gilt. Gegenüber MySQLi und PDO hat die alte MySQL-Extension den Nachteil, dass sie weder Prepared State­ments noch benannte Parameter un­ter­stützt.
  • MySQLi: Bei MySQLi handelt es sich um eine ver­bes­ser­te Version der klas­si­schen PHP-Er­wei­te­rung für den Zugriff auf MySQL-Da­ten­ban­ken. Die Schnitt­stel­le arbeitet sowohl pro­ze­du­ral als auch ob­jekt­ori­en­tiert. Der Einsatz ist auf MySQL- und MariaDB-Da­ten­ban­ken be­schränkt.
  • PDO: Bei PHP Data Objects (PDO) handelt es sich um eine ob­jekt­ori­en­tier­te Schnitt­stel­le, die eine Abs­trak­ti­ons­schicht für den Da­ten­zu­griff zur Verfügung stellt. Somit lassen sich via PDO nicht nur MySQL-Da­ten­ban­ken, sondern auch andere Da­ten­bank­sys­te­me wie Post­greS­QL, Oracle, MSSQL oder SQLite in PHP einbinden.

Im Folgenden werden wir uns auf Da­ten­bank­ver­bin­dun­gen via PDO be­schrän­ken.

Um im Rahmen eines PHP-Scripts Anfragen an eine Datenbank stellen zu können, muss diese zunächst au­then­ti­fi­ziert werden. Eine Da­ten­bank­ver­bin­dung via PDO wird mithilfe folgender Codezeile her­ge­stellt:

<?php
$pdo = new PDO('DSN', 'username', 'password');
?>

Es empfiehlt sich, diese am Anfang eines jeden Scripts ein­zu­fü­gen, das Da­ten­bank­ope­ra­tio­nen enthält.

Wir nutzen das PHP-Schlüs­sel­wort new, um eine Instanz der PDO-Ba­sis­klas­se zu erstellen. Deren Kon­struk­tor erwartet grund­sätz­lich drei Parameter. Den Data Source Name (DSN) sowie einen Be­nut­zer­na­men und das Passwort für die Datenbank – sofern vorhanden. Der DSN besteht in unserem Fall aus folgenden Pa­ra­me­tern:

  • PDO-Da­ten­bank­trei­ber: mysql
  • Da­ten­bank­ser­ver (host=): localhost
  • Da­ten­bank­na­me (dbname=): test
  • Zei­chen­satz (charset=): utf8

Haben Sie für Ihre Datenbank keine Zu­gangs­da­ten definiert, nutzen Sie den Be­nut­zer­na­men root und ein leeres Passwort:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
?>

Die Da­ten­bank­ver­bin­dung wird in der Variable $pdo ge­spei­chert. Diese er­mög­licht Ihnen, im späteren Verlauf des Pro­gramm­codes Bezug auf die Da­ten­bank­ver­bin­dung zu nehmen.

Wurde eine Ver­bin­dung zur Datenbank aufgebaut, können Sie im nach­fol­gen­den Script-Code beliebig viele Abfragen an die Datenbank senden. Beenden Sie das Script, bricht auch die Da­ten­bank­ver­bin­dung ab.

Da­ten­ab­fra­gen mit SELECT, INSERT, UPDATE und DELETE

Um Daten aus unserer Datenbank abzurufen, greifen wir auf die Da­ten­bank­spra­che SQL zurück. Diese ist se­man­tisch an die englische Sprache angelehnt und bewusst einfach gehalten. Die SQL-Syntax ist weit­ge­hend selbst­er­klä­rend.

In SQL arbeitet man mit State­ments, diese werden auch als Abfragen oder Anfragen be­zeich­net.

Eine einfache SELECT-Query besteht bei­spiels­wei­se aus folgenden Kom­po­nen­ten:

SELECT spalte FROM tabelle;

Sie de­fi­nie­ren zunächst den SQL-Befehl SELECT und im Anschluss die je­wei­li­gen Spalten und Tabellen, auf die sich der Befehl beziehen soll. Ein Strich­punkt schließt das Statement ab.

Zudem haben Sie die Mög­lich­keit, das Statement um eine optionale Bedingung sowie eine Sortier- oder Grup­pier­funk­ti­on zu erweitern:

SELECT spalte FROM tabelle WHERE bedingung ORDER BY sortierreihenfolge;

Dabei gilt die Kon­ven­ti­on, dass SQL-Befehle groß­ge­schrie­ben werden, Datenbank-, Tabellen- und Feldnamen hingegen klein. Diese dient lediglich der Les­bar­keit. SQL ist prin­zi­pi­ell eine for­mat­freie Sprache und macht somit keinen Un­ter­schied zwischen Groß- und Klein­schrei­bung.

Nutzen Sie Tabellen- und Spal­ten­na­men die bereits vor­de­fi­nier­ten SQL-Schlüs­sel­wör­tern ent­spre­chen (was nicht zu empfehlen ist), müssen diese in Backticks (``) gesetzt werden.

Wir ver­an­schau­li­chen die Syntax einfacher SQL-State­ments am Beispiel der Befehle SELECT, INSERT, UPDATE und DELETE.

SELECT

Den Befehl SELECT nutzen Sie, um aus­ge­wähl­te Da­ten­rei­hen (rows) aus einer be­lie­bi­gen Anzahl von Tabellen abzurufen. Möchten Sie bei­spiels­wei­se Vor- und Nachnamen sowie die E-Mail-Adressen aller Benutzer in der zuvor er­stell­ten Bei­spiel­ta­bel­le im Web­brow­ser ausgeben, erstellen Sie im Ver­zeich­nis htdocs Ihrer XAMPP-Umgebung eine neue PHP-Datei text.php und fügen folgendes Skript ein:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, email FROM users";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br /><br />";
}
?>

Das Code-Beispiel liest sich wie folgt: Zunächst starten wir das Skript mit dem PHP-Start-Tag <?php. In Zeile 2 stellen wir die Ver­bin­dung zu unserer Datenbank test auf localhost her und speichern diese in der Variable $pdo. Das SQL-Statement mit dem Befehl SELECT findet sich in Zeile 3. Hier wird das Datenbank-Ma­nage­ment-System an­ge­wie­sen, die Spalten forename, surname und email aus der Tabelle users abzurufen. Dieses Statement speichern wir in der Variablen $sql.

Die Zeilen 4 bis 7 zeigen eine foreach-Schleife. Diese bietet uns die Mög­lich­keit, über ein be­lie­bi­ges Array zu iterieren, sprich: eine Da­ten­struk­tur schritt­wei­se durch­zu­ge­hen. Welches Array wir iterieren möchten und wie die ab­ge­frag­ten Daten ge­spei­chert werden sollen, de­fi­nie­ren wir in Klammern hinter dem foreach-Konstrukt:

$pdo->query($sql) as $row

Die Variable $pdo spricht die ge­wünsch­te Datenbank über die in Zeile 2 de­fi­nier­te Ver­bin­dung an. Dieser senden wir mit der Funktion query() das in der Variable $sql ge­spei­cher­te SQL-Statement.

Der Webserver ruft somit die Spalten forename, surname und email der Tabelle users aus der Datenbank test ab und geht im Rahmen einer foreach-Schleife jede einzelne Ta­bel­len­zei­le durch. Wohin die aus­ge­le­se­nen Daten ge­spei­chert werden sollen, definiert das PHP-Schlüs­sel­wort as in der Array-Variable $row.

Im ersten Durchgang der foreach-Schleife sähe dieses Array fol­gen­der­ma­ßen aus.

$row = array (
    forename => John,
    surname => Doe,
    email => john@doe.com
)

Teil der foreach-Schleife ist im aktuellen Beispiel zudem die Text­aus­ga­be via echo in jedem Schlei­fen­durch­gang. Wir gehen somit jede Zeile der Tabelle users einzeln durch, lesen die hin­ter­leg­ten Daten für die im SQL-Statement de­fi­nier­ten Spalten ein und geben diese über den Web­brow­ser aus.

Sollen alle Spalten einer Da­ten­ta­bel­le aus­ge­le­sen werden, verwenden Sie im SQL-Statement den As­te­ris­ken (*) als Platz­hal­ter.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM users";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br />";
    echo "Created at: " . $row['created_at'] . "<br /><br />";
}
?>

Damit haben wir die Mög­lich­keit, alle in users hin­ter­leg­ten Daten im Rahmen des Scripts zu verwenden. In folgendem Screen­shot wurde die Text­aus­ga­be um den Zeit­stem­pel der Ein­trags­er­stel­lung ergänzt:

In beiden vor­her­ge­hen­den Bei­spie­len gibt uns der Webserver Nut­zer­da­ten in der Rei­hen­fol­ge aus, in der wir sie in die Tabelle users ein­ge­tra­gen haben (der ID ent­spre­chend). Möchten Sie die Daten sortiert ausgeben, de­fi­nie­ren Sie die Rei­hen­fol­ge mithilfe des SQL-Schlüs­sel­worts ORDER BY. In folgendem Beispiel werden die Daten gemäß der al­pha­be­ti­schen Rei­hen­fol­ge der Vornamen aus­ge­ge­ben:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, email FROM users ORDER BY forename";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br /><br />";
}
?>

INSERT

Die Er­stel­lung von Da­ten­bank­ein­trä­gen erfolgt nur selten manuell via phpMy­Ad­min. In der Regel werden Daten im Rahmen der Skript­aus­füh­rung vom Webserver in die Datenbank ge­schrie­ben – z. B., wenn ein In­ter­net­nut­zer ein On­line­for­mu­lar auf einer Website ausfüllt oder einen Kun­den­kom­men­tar in einem Webshop hin­ter­lässt. In beiden Fällen kommt im Hin­ter­grund der SQL-Befehl INSERT zum Einsatz. Ein SQL-Statement mit dem Befehl INSERT wird nach folgendem Schema erstellt:

INSERT INTO tabelle (spalte1, spalte2, spalte3) VALUES (wert1, wert2, wert3);

Dieses liest sich wie folgt: Rufe die genannte Tabelle auf und füge die Werte 1, 2 und 3 in die Spalten 1, 2 und 3 ein.

Ein einfaches PHP-Skript, um unserer Be­spiel­ta­bel­le users einen weiteren Datensatz hin­zu­zu­fü­gen, könnte fol­gen­der­ma­ßen aussehen:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "INSERT INTO users (forename, surname, email, password) 
VALUES ('Paddy', 'Irish', 'paddy@irish.com', 'qaywsx')";
if ($pdo->exec($sql) === 1)
    echo "New record created successfully";
?>

Zunächst bauen wir die Ver­bin­dung zur Datenbank auf und speichern diese in der Variable $pdo. An­schlie­ßend de­fi­nie­ren wird das SQL-Statement und speichern dieses in der Variable $sql. In Zeile 5 nutzen wir den Pfeil-Operator (->), um auf die Variable $pdo zu­zu­grei­fen und mithilfe der Funktion exec() das in $sql ge­spei­cher­te SQL-Statement aus­zu­füh­ren.

Um si­cher­zu­ge­hen, dass unser Skript lediglich einen Datensatz in die Tabelle users eingefügt, über­prü­fen wir die Anzahl der be­trof­fe­nen Zeilen mithilfe einer if-Bedingung. Diese sorgt dafür, dass der String New record created suc­cessful­ly nur dann über den Web­brow­ser aus­ge­ge­ben wird, wenn die Anzahl der ein­ge­füg­ten Da­ten­sät­ze 1 beträgt. Wird das Skript erneut aus­ge­führt, bleibt die Meldung aus. Doppelte Einträge ver­hin­dern wir dadurch, dass der Wert email als UNIQUE definiert wurde.

Rufen wir die Übersicht unserer Bei­spiel­ta­bel­le users in der Datenbank test auf, sehen wir, dass die Tabelle um den Datensatz 5 erweitert wurde. Die laufende Iden­ti­fi­zie­rungs­num­mer und der Zeit­stem­pel wurden wie be­ab­sich­tigt au­to­ma­tisch ergänzt.

UPDATE

Möchten Sie einen be­stehen­den Datensatz ak­tua­li­sie­ren, nutzen Sie den SQL-Befehl UPDATE gemäß folgendem Grund­sche­ma:

UPDATE tabelle SET spalte1 = wert1, spalte2 = wert2 WHERE spalte3 = wert3

In Worten aus­ge­drückt bedeutet dieses SQL-Statement: Wähle die an­ge­ge­be­nen Tabelle aus und ersetzte den Wert in Spalte1 durch Wert1 und den Wert in Spalte2 durch Wert2, sofern die Spalte3 den Wert3 enthält. Achtung: Vergessen Sie die Bedingung, über­schreibt MySQL die be­tref­fen­den Felder in allen Da­ten­sät­zen.

Wir haben es somit mit einem SQL-Statement zu tun, das eine Da­ten­bank­ope­ra­ti­on an eine Bedingung knüpft. Über­tra­gen auf unsere Bei­spiel­ta­bel­le, ließe sich die E-Mail-Adresse des Nutzers John Doe durch folgendes PHP-Skript ak­tua­li­sie­ren:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "UPDATE users 
SET email = 'john.doe@example.com', updated_at = NOW() WHERE id = 1";
if ($pdo->exec($sql) === 1)
    echo "Update successful";
?>

Im SQL-Statement de­fi­nie­ren wir, dass der aktuelle Wert in der Spalte email durch den neuen Wert john.doe@example.com ersetzt werden soll, sofern der Wert in der Spalte id 1 beträgt. Wir ak­tua­li­sie­ren somit lediglich den Datensatz mit dem Pri­mär­schlüs­sel 1. Darüber hinaus ak­tua­li­sie­ren wir im selben SQL-Statement den Wert für die Spalte updated_at mithilfe der MySQL-Funktion now(), die den aktuellen Zeit­stem­pel zu­rück­gibt. Aus­ge­führt wird das SQL-Statement wie zuvor mit der Codezeile $pdo->exec($sql) im Rahmen einer if-Bedingung.

War das Update er­folg­reich, sollte Ihnen phpMy­Ad­min im Tab „Browse“ die ak­tua­li­sier­te Tabelle anzeigen:

Im Beispiel haben wir eine E-Mail-Adresse ak­tua­li­siert und den Stan­dard­wert NULL in der Spalte updated_at durch einen Zeit­stem­pel ersetzt. Der UPDATE-Befehl er­mög­licht zudem, Werte aus einer Spalte in eine andere zu über­tra­gen. Einsetzen lässt sich diese Operation bei­spiels­wei­se, wenn wir unsere Bei­spiel­ta­bel­le users um die Spalte email_re­gis­tra­ti­on erweitern. Dies gibt uns die Mög­lich­keit, zwischen zwei E-Mail-Adressen zu un­ter­schei­den: eine, die bei der Re­gis­trie­rung verwendet wurde, und eine aktuelle Kon­takt­adres­se, die sich mit der Zeit ändern kann. Zunächst sind beide Adressen jedoch gleich, sodass wir die Werte von einem Feld ins andere über­tra­gen können. Dazu legen wir mit phpMy­Ad­min via „Add columns“ im Tab „Structure“ zunächst die neue Spalte email_re­gis­tra­ti­on an:

Um die Werte zu über­tra­gen, nutzen wir folgendes UPDATE-Statement:

UPDATE users SET email_registration = email

Da wir alle Da­ten­sät­ze ak­tua­li­sie­ren möchten, for­mu­lie­ren wir keine Bedingung für das Update.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "UPDATE users SET email_registration = email";
$affected = $pdo->exec($sql);
echo "$affected rows updated<br>";
?>

Wird das Skript über den Webserver aus­ge­führt, überträgt das Datenbank-Ma­nage­ment-System die Werte der Spalte email für alle Da­ten­sät­ze in die Spalte email_re­gis­tra­ti­on.

DELETE

Löschen lassen sich Da­ten­bank­ein­trä­ge über den SQL-Befehl DELETE. Diesen nutzen Sie nach folgendem Schema:

DELETE FROM tabelle WHERE spalte = wert

Arbeiten Sie in Ihrer Datenbank mit IDs, bietet es sich an, anhand dieser zu löschende Einträge zu iden­ti­fi­zie­ren. Möchten Sie bei­spiels­wei­se den Eintrag 5 in unserer Bei­spiel­ta­bel­le löschen, gehen Sie wie folgt vor:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "DELETE FROM users WHERE id = 5";
$affected = $pdo->exec($sql);
echo "$affected rows updated<br>";
?>

Der SQL-Befehl DELETE löscht stets eine ganze Da­ten­bank­zei­le. Möchten Sie lediglich Werte in be­stimm­ten Spalten eines Da­ten­sat­zes löschen, rea­li­sie­ren Sie dies mit einem UPDATE-Statement. Mit UPDATE tabelle SET spalte = NULL WHERE … können Sie einer Spalte den Wert NULL zuweisen – vor­aus­ge­setzt natürlich, dass Sie für die be­tref­fen­de Spalte den NULL-Wert zu­ge­las­sen haben.

Prepared State­ments

Mit PDO lassen sich Da­ten­bank­ope­ra­tio­nen als so­ge­nann­te Prepared State­ments umsetzen. Diese „vor­ge­fer­tig­ten Abfragen“ sind heute gängige Praxis in der Web­ent­wick­lung und werden von allen modernen Datenbank-Ma­nage­ment-Systemen un­ter­stützt. In den bis­he­ri­gen Bei­spie­len haben wir Pa­ra­me­ter­wer­te direkt im SQL-Statement übergeben. Prepared State­ments hingegen arbeiten mit Platz­hal­tern, die erst nach­träg­lich mit Werten besetzt werden. Dies er­mög­licht es dem Datenbank-Ma­nage­ment-System, die Gül­tig­keit von Pa­ra­me­tern zu prüfen, bevor diese ver­ar­bei­tet werden. Prepared State­ments bieten, sofern diese im Quelltext konstant umgesetzt werden, einen ef­fek­ti­ven Schutz gegen SQL-Injection. Bei diesem An­griffs­mus­ter erstellen oder verändern Hacker SQL-Befehle, um an sensible Daten zu gelangen, Daten zu über­schrei­ben oder eigene Befehle in ein System ein­zu­brin­gen. SQL-Injection basiert auf einer bekannten Si­cher­heits­lü­cke im Umgang mit SQL-Da­ten­ban­ken: Werden Be­nut­zer­ein­ga­ben bei­spiels­wei­se via $_GET mit sta­ti­schen Pa­ra­me­tern übergeben, bietet dies An­grei­fern die Mög­lich­keit, den Input mit Me­ta­zei­chen an­zu­rei­chern, die zu un­ge­woll­ten Effekten führen können, sofern diese ohne Mas­kie­rung in den SQL-In­ter­pre­ter gelangen. Dem lässt sich durch pa­ra­me­tri­sier­te Abfragen effektiv vorbeugen. Prepared State­ments fungieren dabei als Templates für SQL-Befehle, die getrennt von den ei­gent­li­chen Pa­ra­me­tern an die Datenbank über­mit­telt werden. Diese validiert die über­ge­be­nen Daten, maskiert Me­ta­zei­chen au­to­ma­tisch und fügt die Parameter anstelle der Platz­hal­ter in das SQL-Statement ein. Prepared State­ments bieten neben dem Si­cher­heits­aspekt zudem einen Per­for­mance-Vorteil. Dieser zeigt sich, wenn derselbe SQL-Befehl in einer Schleife mit ver­schie­de­nen Pa­ra­me­tern aus­ge­führt werden soll. Nachdem ein Prepared Statement einmal geparst wurde, liegt dieses im Da­ten­bank­sys­tem vor und muss dort lediglich mit neuen Pa­ra­me­tern aus­ge­führt werden. Kom­pli­zier­te Abfragen lassen sich so deutlich be­schleu­ni­gen. In PDO werden Prepared State­ments mithilfe der Funktion prepare() umgesetzt. Diese bereitet ein Statement für die Aus­füh­rung vor und gibt ein Statement-Objekt zurück. Als Platz­hal­ter für die je­wei­li­gen Werte kommt entweder das Fra­ge­zei­chen (?) oder ein benannter Parameter (Named Parameter) zum Einsatz.

Prepared State­ments mit un­be­nann­ten Pa­ra­me­tern

Folgendes Code­bei­spiel zeigt die Da­ten­bank­ope­ra­ti­on INSERT als Prepared Statement mit un­be­nann­ten Pa­ra­me­tern:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
// SQL-Statement vorbereiten
$statement = $pdo->prepare("INSERT INTO users (forename, surname, email, password) 
VALUES (?, ?, ?, ?)");
// Parameter binden
$statement ->bindParam(1, $forename);
$statement ->bindParam(2, $surname);
$statement ->bindParam(3, $email);
$statement ->bindParam(4, $password);
// Datensätze einfügen
$forename = "Anders";
$surname = "Andersen";
$email = "anders@andersen.com";
$password = "mnbvc";
if ($statement->execute())
    echo "New record $forename created successfully<br>";
$forename = "Matti";
$surname = "Meikäläinen";
$email = "matti@meikäläinen.com";
$password = "jhgfd";
// Status ausgeben
if ($statement->execute())
    echo "New record $forename created successfully<br>";
?>

Zunächst erzeugen wir mithilfe der Funktion prepare() ein Statement-Objekt der ge­wünsch­ten Abfrage und speichern dieses im Array $statement. Statt konkreter Pa­ra­me­ter­wer­te kommt das Fra­ge­zei­chen als Platz­hal­ter zum Einsatz.

Be­inhal­tet ein SQL-Statement lediglich Platz­hal­ter, müssen separat über­ge­be­ne Werte im nach­fol­gen­den Code an dieses gebunden werden. In PHP kommt dazu die Funktion bindParam() zum Einsatz. Wir nutzen den Pfeil-Operator (->), um auf die bindParam()-Methode des Objekts $statement zu­zu­grei­fen und diesem Variablen zu­zu­wei­sen (1 ent­spricht dem ersten Fra­ge­zei­chen, 2 dem zweiten usw.).

Das so erstellte SQL-Template kann nun beliebig oft mit den ge­wünsch­ten Pa­ra­me­tern aus­ge­führt werden. Im aktuellen Beispiel de­fi­nie­ren wir Va­ria­blen­wer­te für zwei Da­ten­sät­ze. Die Aus­füh­rung des vor­ge­fer­tig­ten SQL-State­ments erfolgt für jeden Datensatz via execute().

Prepared State­ments mit benannten Pa­ra­me­tern

Über­sicht­li­cher als der Fra­ge­zei­chen­platz­hal­ter sind benannte Parameter. Dabei handelt es sich um be­nut­zer­de­fi­nier­te Platz­hal­ter, die Sie nach Bedarf nach folgendem Schema benennen können:

:beispiel

Benannte Parameter dürfen weder Leer­zei­chen noch Bin­de­stri­che (-) enthalten. Verwenden Sie statt­des­sen den Un­ter­strich (_).

Folgendes Beispiel zeigt die Da­ten­bank­ope­ra­ti­on INSERT als Prepared Statement mit benannten Pa­ra­me­tern:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
// SQL-Statement vorbereiten
$statement = $pdo->prepare("INSERT INTO users (forename, surname, email, password) 
VALUES (:forename, :surname, :email, :password)");
// Parameter binden
$statement ->bindParam(':forename', $forename);
$statement ->bindParam(':surname', $surname);
$statement ->bindParam(':email', $email);
$statement ->bindParam(':password', $password);
// Datensätze einfügen
$forename = "Anders";
$surname = "Andersen";
$email = "anders@andersen.com";
$password = "mnbvc";
if ($statement->execute())
    echo "New record $forename created successfully<br>";
$forename = "Matti";
$surname = "Meikäläinen";
$email = "matti@meikäläinen.com";
$password = "jhgfd";
$statement->execute();
// Status ausgeben
if ($statement->execute())
    echo "New record $forename created successfully<br>";
?>

Im Prepared Statement finden sich die benannten Parameter :forename, :surname, :email und :password. Diese binden wir via bindParam() an die Variablen $forename, $surname, $email und $password. Im aktuellen Beispiel haben wir sowohl die Parameter als auch die Variablen den Spal­ten­na­men unserer Bei­spiel­ta­bel­le ent­spre­chend benannt. Durch die Syntax vor­ge­ge­ben wird dies nicht. Eine ein­heit­li­che Benennung ist im Sinne eines gut lesbaren Quell­codes jedoch zu empfehlen. Die Zuweisung der Va­ria­blen­wer­te sowie die Aus­füh­rung des SQL-State­ments erfolgt analog zum vor­her­ge­hen­den Beispiel.

Datums- und Zeit­funk­tio­nen in MySQL

MySQL und MariaDB un­ter­stüt­zen diverse Funk­tio­nen, um mit Datums- und Zeit­an­ga­ben zu arbeiten. Eine voll­stän­di­ge Liste finden Sie hier. In unserem MySQL-Tutorial für Ein­stei­ger be­schrän­ken wir uns auf eine Auswahl.

Datums- und Zeit­funk­ti­on Be­schrei­bung
CURRENT_TIMESTAMP() / NOW() Im Bespiel zum SQL-Befehl UPDATE haben Sie bereits die Funktion NOW() ken­nen­ge­lernt. Bei dieser handelt es sich lediglich um ein Synonym der Funktion CURRENT_TIMESTAMP(). Zur Anwendung kommt die Funktion immer dann, wenn im Rahmen einer Da­ten­bank­ope­ra­ti­on das aktuelle Datum inklusive Uhrzeit fest­ge­hal­ten werden soll.
CURDATE() / CURRENT_DATE() Die Funktion CURDATE() gibt das aktuelle Datum zurück.
CURTIME() / CURRENT_TIME() Die Funktion CURTIME() gibt die aktuelle Uhrzeit zurück.
DAY() / DAY­OFMONTH() Gibt den Tag des Monats zurück (0–31); benötigt ein Datum oder Timestamp als Argument
DAYOFWEEK() Gibt den Tag der Woche zurück (1 = Sonntag); benötigt ein Datum oder Timestamp als Argument
MONTH() Gibt den Monat zurück (1–12); benötigt ein Datum oder Timestamp als Argument
YEAR() Gibt eine Jah­res­zahl zurück (1000–9999, 0); benötigt ein Datum oder Timestamp als Argument
DATE() Ex­tra­hiert das Datum aus einer Zeit- oder Da­tums­an­ga­be; benötigt ein Datum oder Timestamp als Argument
TIME() Ex­tra­hiert die Uhrzeit aus seiner Zeit oder Da­tums­an­ga­be; benötigt ein Datum oder Timestamp als Argument
DATE_FORMAT() For­ma­tiert eine Zeit oder Da­tums­an­ga­be gemäß der angegeben Parameter; benötigt ein Datum oder Timestamp als Argument

Ein mögliches An­wen­dungs­sze­na­rio für Zeit- und Da­tums­funk­tio­nen in MySQL sind bei­spiels­wei­se Da­ten­bank­ab­fra­gen, bei denen alle Da­ten­sät­ze aus­ge­le­sen werden sollen, die an einem be­stimm­ten Tag erstellt wurden.

Folgendes Skript gibt uns alle Da­ten­sät­ze unserer Bei­spiel­ta­bel­le users aus, die heute erstellt wurden:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, email FROM users WHERE DATE(created_at) = CURDATE()";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br /><br />";
}
?>

Um lediglich die Einträge von heute aus­zu­ge­ben, nutzen wir im SQL-Statement folgende Bedingung:

DATE(created_at) = CURDATE()

Zunächst ex­tra­hie­ren wir mithilfe der Funktion DATE() das Datum aus dem in der Spalte created_at ge­spei­cher­ten Zeit­stem­pel und gleichen diesen im nächsten Schritt mit dem aktuellen Datum ab. Der Befehl SELECT wählt somit lediglich die Einträge aus, deren Zeit­stem­pel mit dem heutigen Datum über­ein­stim­men.

Al­ter­na­tiv könnten wir den Eintrag auswählen, den wir am 16.12.2016 ak­tua­li­siert haben. Dazu müssen wir lediglich die Bedingung unseres SQL-State­ments anpassen:

SELECT forename, surname, email FROM users WHERE DATE(updated_at) = '2016-12-16'

In diesem Fall wird die aus dem Zeit­stem­pel ex­tra­hier­te Da­tums­an­ga­be mit einem konkreten Datum ab­ge­gli­chen. Darüber hinaus können Sie Abfragen auf ein be­stimm­tes Jahr, einen Monat oder eine Tag ein­gren­zen.

Folgendes Statement bezieht sich auf alle Einträge in der Tabelle users, die im Dezember erstellt wurden:

SELECT forename, surname, email FROM users WHERE month(created_at) = 12";

Neben dem Gleich­heits­zei­chen un­ter­stützt SQL in Be­din­gun­gen zudem folgende Ver­gleichs­ope­ra­to­ren:

Ver­gleichs­ope­ra­tor Be­schrei­bung
= ist gleich
< kleiner als
> größer als
<= kleiner oder gleich
>= größer oder gleich
!= ungleich

Darüber hinaus lassen sich mehrere Be­din­gun­gen mit logischen Ope­ra­to­ren ver­knüp­fen:

Logischer Operator Be­schrei­bung
OR bzw. Logisches Oder
AND bzw. && Logisches Und

Folgendes Statement wählt bei­spiels­wei­se alle Einträge aus, die nach Februar und vor April erstellt wurden:

SELECT forename, surname, email FROM users WHERE MONTH(created_at) > 2 AND MONTH(created_at) < 4";

Bisher haben wir Datums- und Zeit­an­ga­ben in unserer Datenbank im vor­ge­ge­ben Format ge­spei­chert. Mit MySQL und MariaDB sind Sie auf dieses jedoch nicht fest­ge­legt. Die Funktion DATE_FORMAT() bietet Ihnen die Mög­lich­keit, Daten und Uhrzeiten mit op­tio­na­len Pa­ra­me­tern beliebig zu for­ma­tie­ren.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, DATE_FORMAT(created_at,'%D %M %Y') AS created_at FROM users WHERE id=1";
    foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . " your profile was created at: " . $row['created_at'] . ".<br /><br />";
}
?>

Die nach­ste­hen­de Tabelle zeigt mögliche Parameter für die Funktion DATE_FORMAT() gemäß MySQL-Do­ku­men­ta­ti­on.

Parameter Be­schrei­bung Wer­te­be­reich/Beispiele
%c Nu­me­ri­sche Mo­nats­an­ga­be mit 1 oder 2 Stellen 0 bis 12
%d Nu­me­ri­sche Angabe des Tags im Monat mit 2 Stellen 00 bis 31
%e Nu­me­ri­sche Angabe des Tags im Monat mit 1 oder 2 Stellen 0 bis 31
%H Stun­den­an­zei­ge mit 2 Stellen 00 bis 23
%i Nu­me­ri­sche Mi­nu­ten­an­ga­be mit 2 Stellen 00 bis 59
%j Nu­me­ri­sche Angabe des Tags im Jahr mit 3 Stellen 001 bis 366
%k Nu­me­ri­sche Stun­den­an­ga­be mit 1 oder 2 Stellen 0 bis 23
%M Name des Monats im aktuellen Ge­biets­sche­ma January, February, March etc.
%m Nu­me­ri­sche Mo­nats­an­ga­be mit 2 Stellen 00 bis 12
%s Nu­me­ri­sche Se­kun­den­an­zei­ge mit 2 Stellen 00 bis 59
%T Zeit im 24-Stun­den­fo­mat (Kurzform für '%H:%i:%S'. HH:MM:SS
%u Nummer der Woche im Jahr, beginnend mit Montag 00 bis 53
%W Name des Wo­chen­tags im aktuellen Ge­biets­sche­ma Monday, Tuesday etc.
%w Wochentag 0 = Sunday, 6 = Saturday
%Y Jah­res­zahl mit 4 Stellen z. B. 2016
%y Jah­res­zahl mit 2 Stellen z. B. 16

MySQL-Feh­ler­mel­dun­gen

Lässt sich ein Skript nicht wie gewünscht ausführen, liegt dies in der Regel an syn­tak­ti­schen Fehlern im Quellcode oder falsch benannten Tabellen, Spalten und Variablen. Der Da­ten­bank­ser­ver spielt in diesem Fall jedoch nicht zwangs­läu­fig eine Feh­ler­mel­dung aus. Oft bleibt das ge­wünsch­te Ergebnis ohne Hinweis auf die fehl­ge­schla­ge­ne Operation aus.

Mit errorInfo() stellt PDO daher eine Funktion bereit, mit der sich er­wei­ter­te Feh­ler­infor­ma­tio­nen zur letzten Da­ten­bank­ope­ra­ti­on gezielt abrufen lassen – bei­spiels­wei­se, um diese über den Web­brow­ser aus­zu­ge­ben.

In folgendem Skript zur Ak­tua­li­sie­rung der E-Mail-Adresse kommt die Funktion errorInfo() in Kom­bi­na­ti­on mit einer if-Schleife zum Einsatz. Deren Bedingung ist die korrekte Aus­füh­rung des SQL-State­ments. Wird dieses feh­ler­frei aus­ge­führt, gibt der Webserver den String Update suc­cessful aus. An­dern­falls wird der unter else auf­ge­führ­te Code aus­ge­führt.

Im aktuellen Beispiel in­for­mie­ren wir den Nutzer darüber, dass ein SQL-Fehler auf­ge­tre­ten ist und geben das be­tref­fen­de SQL-Statement sowie er­wei­ter­te Feh­ler­infor­ma­tio­nen via errorInfo() aus:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$statement = $pdo -> prepare("UPDATE user SET password = :password, updated_at = NOW() WHERE id = 1");
$statement ->bindParam(':password', $password);
$password = "YXDli_89%s";
if ($statement ->execute()){
    echo "Update successful";
} else {
    echo "SQL Error <br />";
    echo $statement->queryString."<br />";
    echo $statement->errorInfo()[2];
}
?>

Führen wir das Skript über den Webserver aus, bekommen wir folgende In­for­ma­ti­on aus­ge­ge­ben:

SQL Error 
UPDATE user SET password = :password, updated_at = NOW() WHERE id = 1
Table 'test.user' doesn't exist

Der SQL-Befehl UPDATE bezieht sich auf eine Tabelle mit dem Namen user. Unsere Bei­spiel­ta­bel­le haben wir jedoch mit users benannt. Der SQL-Server kann die an­ge­for­der­te Tabelle daher nicht finden und gibt die Meldung Table 'test.user' doesn't exist aus. Feh­ler­ur­sa­che ist somit lediglich ein Tipp­feh­ler im SQL-Statement, der schnell behoben ist.

Bei den Rück­ga­be­wer­ten der Funktion errorInfo() handelt es sich um ein Array, das drei Elemente enthält:

[0] = SQL-Feh­ler­code

[1] = Trei­ber­spe­zi­fi­scher Feh­ler­code

[2] = Trei­ber­spe­zi­fi­sche Feh­ler­mel­dung

Welche In­for­ma­tio­nen Sie via errorInfo() abrufen, de­fi­nie­ren Sie durch die Angabe des ge­wünsch­ten Elements in eckigen Klammern.

In der Praxis werden de­tail­lier­te Feh­ler­infor­ma­tio­nen jedoch nur selten über den Web­brow­ser aus­ge­ge­ben. End­an­wen­der können mit diesen Angaben in der Regel nur wenig anfangen. Po­ten­zi­el­le Angreifer hingegen nutzen Feh­ler­mel­dun­gen mitunter, um SQL-Abfragen nach­zu­voll­zie­hen und so Schwach­stel­len einer Anwendung ausfindig zu machen. Es empfiehlt sich daher, Nutzer nur allgemein über einen Feh­ler­fall zu in­for­mie­ren und konkrete Feh­ler­infor­ma­tio­nen intern zu speichern. Umsetzen lässt sich dies bei­spiels­wei­se fol­gen­der­ma­ßen:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$statement = $pdo->prepare("UPDATE user SET password = :password, updated_at = NOW() WHERE id = 1");
$statement->bindParam(':password', $password);
$password = "YXDli_89%s";
if ($statement->execute()) {
     echo "Update successful";
} else {
    echo "Leider ist ein Fehler bei der Aktualisierung Ihres Passworts aufgetreten. Bitte kontaktieren Sie unseren Administrator unter: admin@website.de.";
    $error = DATE("Y-m-d H:i:s") . " - " . __FILE__ . " - " . $statement->queryString . " - " . $statement->errorInfo()[2] . " \r\n";
    file_put_contents("sqlerrors.log", $error, FILE_APPEND); 
}
?>

Statt den Rück­ga­be­wert der Funktion errorInfo() via echo im Web­brow­ser aus­zu­ge­ben, speichern wir diesen zusammen mit dem aktuellen Zeit­stem­pel sowie dem Pfad zur Datei und dem be­tref­fen­den SQL-Statement in der Variable $error.

Die PHP-Funktion DATE() gibt den Unix-Zeit­stem­pel im an­ge­ge­be­nen Format aus. Die so­ge­nann­te magische Konstante __FILE__ liefert uns den voll­stän­di­gen Pfad zur Datei test.php. Das aktuelle SQL-Statement rufen wir wie im vor­he­ri­gen Beispiel via $statement->queryString ab. Im Anschluss werden die in $error ge­spei­cher­ten Daten mithilfe der Funktion file_put_contents() als Text in der Datei sqlerrors.log im Ordner htdocs ge­spei­chert.

JOIN

Wie im Kapitel zu re­la­tio­na­len Da­ten­ban­ken an­ge­spro­chen, können wir Daten aus mehreren Tabellen gleich­zei­tig abfragen. Nachdem Sie nun mit den grund­le­gen­den Da­ten­bank­ope­ra­tio­nen vertraut sind, greifen wir dies erneut auf und de­mons­trie­ren Ihnen, wie Sie ver­schie­de­ne Tabellen Ihrer Datenbank in einem Join (dt. „Ver­bin­dung“ oder „Verbund“) ver­knüp­fen.

Die Zu­sam­men­füh­rung von Tabellen im Rahmen einer Abfrage erfolgt mithilfe des SQL-Befehls JOIN. Dabei werden zwei oder mehr nor­ma­li­sier­te Tabellen über ge­mein­sa­me Spalten verbunden. Rea­li­sie­ren lässt sich dies via Fremd­schlüs­sel.

Wir de­mons­trie­ren die Ver­knüp­fung von Da­ten­bank­ta­bel­len an folgendem Beispiel:

Die Tabelle zeigt eine Auswahl unserer Lieb­lings­songs aus den 1960er Jahren und soll als Ne­ga­tiv­bei­spiel für ein schlech­tes Da­ten­bank­de­sign dienen.

Auf den ersten Blick fällt auf, dass die Tabelle zahl­rei­che red­un­dan­te Da­ten­fel­der aufweist. Diese be­sei­ti­gen wir, in dem wir die Daten im Rahmen der Nor­ma­li­sie­rung in separate Tabellen aufteilen und mithilfe von Fremd­schlüs­seln ver­knüp­fen.

Nor­mal­for­men

Gutes Da­ten­bank­de­sign zeichnet sich durch ein Minimum an Redundanz aus. Vermeiden lassen sich doppelte Einträge durch die Nor­ma­li­sie­rung von Da­ten­ta­bel­len. Im Rahmen des re­la­tio­na­len Da­ten­bank­mo­dells haben sich drei auf­ein­an­der auf­bau­en­de Nor­mal­for­men etabliert, die Ihnen feste Regeln für die optimale Da­ten­struk­tu­rie­rung vorgeben.

1. Nor­mal­form

Eine Tabelle ent­spricht der 1. Nor­mal­form, wenn alle At­tri­but­wer­te atomar sind. Als atomar gelten At­tri­but­wer­te, wenn diese lediglich eine In­for­ma­ti­on enthalten. Ver­deut­li­chen lässt sich dies an unserem Ne­ga­tiv­bei­spiel.

Be­trach­ten Sie bei­spiels­wei­se die Spalten album_title und interpret in der Tabelle album. Statt jede in der Tabelle ent­hal­te­ne In­for­ma­ti­on in einer separaten Spalte auf­zu­füh­ren, haben wir es uns ver­meint­lich leicht gemacht und In­for­ma­tio­nen zum Er­schei­nungs­jahr eines Albums sowie zum Bestehen einer Band einfach in Klammern hinter dem Al­bum­ti­tel bzw. der In­ter­pre­ten­an­ga­be notiert. Doch diese Schlud­rig­keit rächt sich spä­tes­tens dann, wenn wir bei­spiels­wei­se alle Titel abfragen wollen, die in einem be­stimm­ten Jahr ver­öf­fent­licht wurden.

Wir empfehlen daher Da­ten­ta­bel­len stets nach den Regeln der 1. Nor­mal­form zu erstellen. Für unser Ta­bel­len­bei­spiel würde dies fol­gen­der­ma­ßen aussehen:

album
album_id album_title released interpret years_active track title
1 Revolver 1966 The Beatles 1960–1970 2 Eleanor Rigby
1 Revolver 1966 The Beatles 1960–1970 8 Good Day Sunshine
2 Abbey Road 1969 The Beatles 1960–1970 1 Come Together
2 Abbey Road 1969 The Beatles 1960–1970 7 Here Comes the Sun
3 Beggars Banquet 1968 The Rolling Stones 1962–present 1 Sympathy for the Devil
3 Beggars Banquet 1968 The Rolling Stones 1962–present 6 Street Fighting Man
4 Sticky Fingers 1971 The Rolling Stones 1962–present 1 Brown Sugar
4 Sticky Fingers 1971 The Rolling Stones 1962–present 3 Wild Horses

Sämtliche Daten sind nun separiert und somit ohne weiteres auslesbar. Unsere Tabelle enthält jedoch nach wie vor Red­un­dan­zen. Wie sich diese be­sei­ti­gen lassen, zeigen wir in den nächsten Schritten.

2. Nor­mal­form

Eine Tabelle ent­spricht der 2. Nor­mal­form, wenn alle Be­din­gun­gen der 1. Nor­mal­form erfüllt sind und jedes Nicht­schlüs­sel­at­tri­but vom gesamten Pri­mär­schlüs­sel der Tabelle voll funk­tio­nal abhängig ist.

Oft enthalten Da­ten­ta­bel­len lediglich eine Spalte, die als Pri­mär­schlüs­sel fungiert. Tabellen dieser Art ent­spre­chen au­to­ma­tisch der 2. Nor­mal­form, wenn die Be­din­gun­gen der 1. Nor­mal­form erfüllt sind. Mitunter kommt es aber vor, dass sich der Pri­mär­schlüs­sel einer Tabelle aus zwei Spalten ergibt. Dies ist bei unserem Ta­bel­len­bei­spiel der Fall.

Um einen ge­wünsch­ten Titel aus der title-Spalte der Tabelle title abzurufen, bräuchten wir zum einen die album_id als auch die in der Spalte track hin­ter­leg­te Ti­tel­num­mer. Der Titel Sympathy for the Devil ließe sich bei­spiel­wei­se durch den Pri­mär­schlüs­sel album_ID = 3 AND track = 1 abrufen. Wir haben es also mit einem zu­sam­men­ge­setz­ten Pri­mär­schlüs­sel zu tun. Dieser wird jedoch aus­schließ­lich für Abfragen benötigt, die sich auf die title-Spalte beziehen. Die Spalten album_title, released, interpret und years_active sind aus­schließ­lich von der album_id abhängig. Es besteht für diese Spalten somit keine volle funk­tio­na­le Ab­hän­gig­keit vom gesamten Pri­mär­schlüs­sel. Die Be­din­gun­gen der 2. Nor­mal­form sind nicht erfüllt.

Dies lässt sich ändern, indem wir die Spalte title in eine neue Tabelle auslagern und durch einen Fremd­schlüs­sel (album_id) mit der Aus­gangs­ta­bel­le ver­knüp­fen:

album
album_id album_title released interpret years_active
1 Revolver 1966 The Beatles 1960–1970
2 Abbey Road 1969 The Beatles 1960–1970
3 Beggars Banquet 1968 The Rolling Stones 1962–present
4 Sticky Fingers 1971 The Rolling Stones 1962–present
title
album_id track title
1 2 Eleanor Rigby
1 8 Good Day Sunshine
2 1 Come Together
2 7 Here Comes the Sun
3 1 Sympathy for the Devil
3 6 Street Fighting Man
4 1 Brown Sugar
4 3 Wild Horses

Die über­ar­bei­te­te Tabelle album be­inhal­tet lediglich einen ein­tei­li­gen Pri­mär­schlüs­sel und erfüllt somit au­to­ma­tisch die Be­din­gun­gen der 2. Nor­mal­form. Die neue Tabelle title be­inhal­tet lediglich die Nicht-Schlüs­sel­spal­te title. Diese ist von beiden Teilen des Pri­mär­schlüs­sels (album_id und track) voll funk­tio­nal abhängig und ent­spricht somit ebenfalls der 2. Nor­mal­form.

Doch auch in der 2. Nor­mal­form enthält unsere Da­ten­ta­bel­le album red­un­dan­te Einträge.

3. Nor­mal­form

Soll eine Tabelle der 3. Nor­mal­form ent­spre­chen, müssen alle Be­din­gun­gen der 2. Nor­mal­form (und somit auch der 1. Nor­mal­form) erfüllt sein. Zudem gilt, dass kein Nicht­schlüs­sel­at­tri­but in tran­si­ti­ver Ab­hän­gig­keit zu einem Schlüs­sel­at­tri­but stehen darf. Diese Bedingung hört sich kom­pli­ziert an, lässt sich jedoch mit wenigen Worten erklären: Eine tran­si­ti­ve Ab­hän­gig­keit besteht immer dann, wenn ein Nicht­schlüs­sel­at­tri­but von einem anderen Nicht­schlüs­sel­at­tri­but abhängt.

Genau dies trifft auf unsere Bei­spiel­ta­bel­le album zu. Hier finden sich die Spalten interpret und years_active. Während sich der Interpret durch die album_id ermitteln lässt, ist die Jah­res­an­ga­be des Band­be­stehens wiederum vom In­ter­pre­ten und somit transitiv von der album_id abhängig. Dies hat den Nachteil, dass das Datenbank-Ma­nage­ment-System jedes Mal, wenn ein neues Album eines bereist auf­ge­führ­ten In­ter­pre­ten ein­ge­tra­gen wird, au­to­ma­tisch einen red­un­dan­ten Wert in der Spalte years_active ab­spei­chert.

Um die Be­din­gun­gen der 3. Nor­mal­form zu erfüllen und somit alle Red­un­dan­zen aus unserer Tabelle zu entfernen, müssen wir auch die Spalte interpret inklusive years_active in eine separate Tabelle auslagern und via Fremd­schlüs­sel (interpret_id) mit der Aus­gangs­ta­bel­le album ver­knüp­fen.

Wir erhalten somit drei nor­ma­li­sier­te Tabellen: interpret, album und title.

interpret
interpret_id interpret years_active
1 The Beatles 1960–1970
2 The Rolling Stones 1962–present
album
album_id interpret_id album_title released
1 1 Revolver 1966
2 1 Abbey Road 1969
3 2 Beggars Banquet 1968
4 2 Sticky Fingers 1971
title
album_id track title
1 2 Eleanor Rigby
1 8 Good Day Sunshine
2 1 Come Together
2 7 Here Comes the Sun
3 1 Sympathy for the Devil
3 6 Street Fighting Man
4 1 Brown Sugar
4 3 Wild Horses

Möchten wir nun einen be­stimm­ten Titel in unserer Datenbank inklusive der In­for­ma­tio­nen zum Album sowie zum In­ter­pre­ten ausgeben, müssen wir die drei separaten Tabellen mithilfe des SQL-Befehls JOIN und der je­wei­li­gen Fremd­schlüs­sel verbinden.

Fremd­schlüs­sel via phpMy­Ad­min de­fi­nie­ren

Sofern Sie als Datenbank-Engine InnoDB gewählt haben, de­fi­nie­ren Sie Fremd­schlüs­sel­be­zie­hun­gen bequem über die grafische Be­nut­zer­ober­flä­che Ihrer Ad­mi­nis­tra­ti­ons­soft­ware phpMy­Ad­min. Dabei kann der Pri­mär­schlüs­sel einer Tabelle als Fremd­schlüs­sel in beliebig vielen anderen Tabellen zum Einsatz kommen.

In unserem Beispiel benötigen wir zwei Ver­bin­dun­gen, um die drei nor­ma­li­sier­ten Tabellen album, interpret und title zu ver­knüp­fen:

  • Für die erste Ver­bin­dung nutzen wir den Pri­mär­schlüs­sel album_id der Tabelle album als Fremd­schlüs­sel in der Tabelle title.
  • Für die zweite Ver­bin­dung nutzen wir den Pri­mär­schlüs­sel interpret_id der Tabelle interpret als Fremd­schlüs­sel in der Tabelle album.

Ver­an­schau­li­chen lassen sich die Fremd­schlüs­sel­be­zie­hun­gen durch folgende Grafik:

Bei der Ver­knüp­fung von Da­ten­ta­bel­len gilt: Eine Spalte, die als Fremd­schlüs­sel fungieren soll, muss mit den At­tri­bu­ten UNIQUE oder INDEX versehen werden.

Die Beziehung zwischen Pri­mär­schlüs­sel und Fremd­schlüs­sel ent­spricht in der Regel dem Be­zie­hungs­typ 1:n. Jedes Datenfeld in der Pri­mär­schlüs­sel-Spalte der Tabelle A steht mit beliebig vielen (n) Da­ten­fel­dern in der Fremd­schlüs­sel-Spalte der Tabelle B in Beziehung. Doch jedes Datenfeld in der Fremd­schlüs­sel-Spalte der Tabelle B bezieht sich immer auf genau ein Datenfeld in der Pri­mär­schlüs­sel-Spalte der Tabelle A. Bei­spiels­wei­se haben wir in der Pri­mär­schlüs­sel-Spalte album_id in der Tabelle album vier Einträge, die über den Fremd­schlüs­sel title.album_id mit acht Einträgen der Tabelle title verknüpft sind.

Um die ge­wünsch­ten Ver­knüp­fun­gen ein­zu­rich­ten, legen wir die Tabellen album, interpret und title in phpMy­Ad­min an und de­fi­nie­ren unsere Pri­mär­schlüs­sel im Rahmen der Ta­bel­len­er­stel­lung wie bereits be­schrie­ben über die Option „Index“. Achten sie zudem darauf, dass Spalten, die später als Fremd­schlüs­sel fungieren sollen, ebenfalls über die Index-Option als INDEX oder UNIQUE markiert werden. Für 1:n-Be­zie­hun­gen eignet sich aber nur INDEX, denn die Werte in einem UNIQUE-Feld dürfen sich schließ­lich nicht wie­der­ho­len.

Im nächsten Schritt de­fi­nie­ren wir die Fremd­schlüs­sel. Wir de­mons­trie­ren dies am Beispiel der Tabelle album. Dazu wählen wir die Tabelle im Na­vi­ga­ti­on-Panel aus und rufen den Tab Structure in der Me­nü­leis­te auf. Hier findet sich die Schalt­flä­che „Relation view“ (Be­zie­hungs­an­sicht):

Fremd­schlüs­sel­be­zie­hun­gen de­fi­nie­ren Sie in der Be­zie­hungs­an­sicht einer Da­ten­ta­bel­le über das Ein­ga­be­feld „Foreign key cons­traints“:

In der Tabelle album soll die Spalte interpret_id als Fremd­schlüs­sel fungieren, der auf dem Pri­mär­schlüs­sel interpret_id der Tabelle interpret beruht.

Wir wählen somit im Dropdown-Menü unter „Column“ die Spalte interpret_id als Fremd­schlüs­sel aus. Beachten Sie, dass hier nur Spalten auf­ge­führt werden, die als INDEX, UNIQUE oder PRIMARY markiert wurden. Im drei­tei­li­gen Ein­ga­be­feld „Foreign key cons­traint (InnoDB)“ bestimmen wir, auf welchem Pri­mär­schlüs­sel welcher Tabelle in welcher Datenbank unser Fremd­schlüs­sel beruhen soll. Wir wählen wie folgt:

Datenbank: test

Tabelle: interpret

Pri­mär­schlüs­sel: interpret_id

Das Feld „Cons­traint name“ kann leer bleiben. Das Datenbank-Ma­nage­ment-System vergibt hier au­to­ma­tisch einen Namen. De­fi­nie­ren müssen Sie jedoch, wie sich eine Tabelle mit Fremd­schlüs­sel verhält, sobald der dem Fremd­schlüs­sel zu­grun­de­lie­gen­de Pri­mär­schlüs­sel verändert oder gelöscht wird.

Wird bei­spiels­wei­se ein Interpret aus der El­tern­ta­bel­le interpret gelöscht, ver­schwin­det auch der mit diesem Eintrag ver­bun­de­ne Pri­mär­schlüs­sel. Es gilt somit zu klären, was mit Einträgen passieren soll, die sich auf diesen Eintrag via Fremd­schlüs­sel beziehen – in unserem Beispiel also Alben eines In­ter­pre­ten.

Um das Verhalten einer Tabelle mit Fremd­schlüs­sel im Fall eines UPDATE oder DELETE zu bestimmen, stehen in MySQL bzw. MariaDB vier Optionen zur Verfügung.

  • RESTRICT: Die Option RESTRICT schließt eine Ver­än­de­rung der El­tern­ta­bel­le aus, sofern andere Tabellen exis­tie­ren, die auf diese verweisen. In unserem Fall könnte ein Datensatz in der Tabelle interpret nicht gelöscht werden, wenn Da­ten­sät­ze in der Tabelle album mit diesem verknüpft sind.
  • CASCADE: Die Option CASCADE sorgt dafür, dass eine Ver­än­de­rung in der El­tern­ta­bel­le an alle Tabellen wei­ter­ge­ben wird, die sich auf diese El­tern­ta­bel­le beziehen. Setzen wir bei­spiels­wei­se die interpret_id des In­ter­pre­ten The Rolling Stones von 2 auf 8, wird diese Änderung bei der Foreign-Key-Option CASCADE auch für alle Tabellen über­nom­men, die die interpret_id als Fremd­schlüs­sel nutzen. Wird ein Eintrag in der El­tern­ta­bel­le gelöscht, führt dies dazu, dass auch alle mit diesem Eintrag ver­bun­de­nen Da­ten­sät­ze in anderen Tabellen gelöscht werden. Achtung: Auf diese Weise kann das Löschen eines einzelnen Eintrags das Ver­schwin­den zahl­rei­cher Da­ten­sät­ze nach sich ziehen.
  • SET NULL: Wählen Sie die Option SET NULL, wird der Wert in der Fremd­schlüs­sel­spal­te auf NULL gesetzt, sobald sich der Pri­mär­schlüs­sel in der El­tern­ta­bel­le ändert oder dieser gelöscht wird.
  • NO ACTION: In MySQL ist die Option NO ACTION ein Äqui­va­lent der Option RESTRICT.

Haben Sie die ge­wünsch­te Option für die Fremd­schlüs­sel­be­zie­hung definiert, be­stä­ti­gen Sie Ihre Eingabe mit einem Klick auf „Save“. Das Datenbank-Ma­nage­ment-System vergibt au­to­ma­tisch einen Namen für die neu angelegte Beziehung.

JOIN-Typen in MySQL und MariaDB

Fremd­schlüs­sel­be­zie­hun­gen er­mög­li­chen es Ihnen, Daten aus ver­schie­de­nen Tabellen gleich­zei­tig mit nur einem SQL-Statement abzurufen. Dazu stehen Ihnen in MySQL und MariaDB vier JOIN-Typen zur Verfügung:

  • INNER JOIN: Bei einem INNER JOIN sucht das Datenbank-Ma­nage­ment-System nach ge­mein­sa­men Einträgen in beiden via JOIN verbunden Tabellen. Es werden nur die Da­ten­sät­ze ein­ge­le­sen, in denen es Über­ein­stim­mun­gen gibt, sprich: in denen die Werte in den ver­knüp­fen­den Spalten (Pri­mär­schlüs­sel und Fremd­schlüs­sel) in beiden Tabellen über­ein­stim­men.
  • OUTER JOIN: Bei einem OUTER JOIN un­ter­schie­det man zwischen linker und rechter Da­ten­ta­bel­le. Anders als beim INNER JOIN werden nicht nur die Da­ten­sät­ze ein­ge­le­sen, bei denen sich Über­ein­stim­mun­gen in beiden Tabellen finden, sondern zu­sätz­lich auch alle übrigen Da­ten­sät­ze der rechten bzw. der linken Tabelle.
  • LEFT JOIN: Es werden alle Da­ten­sät­ze der linken Tabelle ein­ge­le­sen sowie alle Da­ten­sät­ze der rechten Tabelle, in der sich Über­ein­stim­mun­gen finden.
  • RIGHT JOIN: Es werden alle Da­ten­sät­ze der rechten Tabelle ein­ge­le­sen sowie alle Da­ten­sät­ze der linken Tabelle, in der sich Über­ein­stim­mun­gen finden.

Im Rahmen unseres MySQL-Ein­stei­ger-Tutorials be­schrän­ken wir auf den INNER JOIN.

Die Syntax eines INNER JOINs ent­spricht folgendem Grund­sche­ma:

SELECT * FROM tabelle1
INNER JOIN tabelle2 ON tabelle1.fremdschlüssel = tabelle2.primärschlüssel
WHERE spalte = wert

Der SQL-Befehl SELECT in Kom­bi­na­ti­on mit dem Platz­hal­ter * weist das Datenbank-Ma­nage­ment-System an, die Werte aller Spalten aus­zu­le­sen, für die die Be­din­gun­gen der ON- und der WHERE-Klausel gelten.

Da es sich um einen INNER JOIN handelt, werden nur die Da­ten­sät­ze, bei denen sich eine Über­ein­stim­mung zwischen dem Fremd­schlüs­sel der tabelle1 und dem Pri­mär­schlüs­sel der tabelle2 findet, aus der Datenbank geholt. Darüber hinaus lässt sich mithilfe der WHERE-Klausel eine optionale Fil­ter­funk­ti­on de­fi­nie­ren.

Wir ver­deut­li­chen dies an einem Beispiel, das sich auf unsere nor­ma­li­sier­ten Tabellen album, interpret und title bezieht:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id";
foreach ($pdo->query($sql) as $row) {
    echo $row['album_title'] . " was released by " . $row['interpret'] . " in " . $row['released'] . ".<br /><br />";
}
?>

Das Bei­spiel­skript zeigt einen INNER JOIN, bei dem die Tabelle album mit der Tabelle interpret verbunden wird. Wir lesen lediglich die Da­ten­sät­ze aus, bei denen eine Über­ein­stim­mung zwischen Pri­mär­schlüs­sel und Fremd­schlüs­sel besteht.

album.interpret_id = interpret.interpret_id

In unserem Da­ten­be­stand sind das alle Da­ten­sät­ze (ein LEFT oder RIGHT JOIN hätte daher das gleiche Ergebnis gehabt). Im Anschluss geben wir die ein­ge­le­se­nen Werte mithilfe einer foreach-Schleife und dem Sprach­kon­strukt echo im Browser aus.

Im Rahmen eines SQL-State­ments haben wir somit die In­ter­pre­ten­an­ga­be aus der Tabelle interpret und die Angaben zum Al­bum­ti­tel und zum Er­schei­nungs­jahr der Platte aus der Tabelle album aus­ge­le­sen.

Welche Da­ten­sät­ze des Joins aus­ge­ge­ben werden, lässt sich mit einer Bedingung in der WHERE-Klausel ein­schrän­ken. Möchten wir bei­spiels­wei­se lediglich die Alben ausgeben, die im Jahr 1968 er­schie­nen sind, können wir fol­gen­der­ma­ßen vorgehen:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id WHERE released = 1968";
foreach ($pdo->query($sql) as $row) {
    echo $row['album_title'] . " was released by " . $row['interpret'] . " in " . $row['released'] . ".<br /><br />";
}
?>

Durch die Bedingung WHERE released = 1968 schränken wir die Ausgabe im Browser auf ein Album ein. Beggars Banquet von den Rolling Stones ist das einzige Album in unserer noch recht über­schau­ba­ren Datenbank, das im Jahr 1968 ver­öf­fent­licht wurde.

Mithilfe des JOIN-Befehls lassen sich beliebig viele Tabellen zur einen Da­ten­ver­bund zu­sam­men­schlie­ßen. Im folgenden Beispiel ver­knüp­fen wir die Tabelle album in einem INNER JOIN mit den Tabellen interpret und title, um uns sämtliche In­for­ma­tio­nen zu den in der Datenbank ge­spei­cher­ten Mu­sik­ti­teln ausgeben zu lassen.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id 
INNER JOIN title ON album.album_id = title.album_id";
foreach ($pdo->query($sql) as $row) {
    echo $row['title'] . " was released by " . $row['interpret'] . " as Track " . $row['track'] . " on " . $row['album_title'] . " in " . $row['released'] . ".<br /><br />";
}
?>

Auch hier können wir nach Bedarf eine WHERE-Klausel mit einer Fil­ter­funk­ti­on de­fi­nie­ren. Bei­spiels­wei­se, wenn wir lediglich die In­for­ma­tio­nen zu Track 7 auf dem Album „Abbey Road“ ausgeben möchten.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id 
INNER JOIN title ON album.album_id = title.album_id WHERE track = 7 AND album_title = 'Abbey Road'";
foreach ($pdo->query($sql) as $row) {
    echo $row['title'] . " was released by " . $row['interpret'] . " as Track " . $row['track'] . " on " . $row['album_title'] . " in " . $row['released'] . ".<br /><br />";
}
?>

Beachten Sie, dass wir es bei der Tabelle title mit einem mehr­tei­li­gen Pri­mär­schlüs­sel zu tun haben. Möchten wir auf einen be­stimm­ten Titel verweisen, benötigen wir neben der Track-Nummer die album_id, die zusammen mit dem Titel des Albums in der Tabelle album hin­ter­legt ist.

Vom Ein­stei­ger zum Profi

Unser MySQL-Tutorial für Ein­stei­ger versteht sich als Crashkurs, der Ihnen die Grund­la­gen SQL-basierter Da­ten­bank­sys­te­me nä­her­bringt und an pra­xis­re­le­van­ten Bei­spie­len einfache Da­ten­bank­ope­ra­tio­nen ver­an­schau­licht. Sollte Ihr Interesse über die hier dar­ge­stell­ten An­wen­dungs­mög­lich­kei­ten hin­aus­ge­hen, empfehlen wir die bereits im Ein­füh­rungs­ka­pi­tel ver­link­ten Do­ku­men­ta­tio­nen der DBMS MySQL und MariaDB. Außerdem finden Sie im Internet zahl­rei­che Websites, die Tutorials und An­wen­dungs­bei­spie­le zum beliebten Datenbank-Ma­nage­ment-System anbieten. Zu empfehlen ist zudem die In­ter­net­platt­form Stack Overflow, auf der sich eine rege Nut­zer­com­mu­ni­ty von mehr als 6,5 Millionen Ent­wick­lern über aktuelle Fragen und Pro­blem­stel­lun­gen der Software-Ent­wick­lung aus­tauscht. Und natürlich stehen Ihnen auch hier im IONOS Digital Guide viele wei­ter­füh­ren­de Artikel zum Thema Da­ten­ban­ken zur Verfügung – Sie finden sie über die unten auf­ge­führ­ten Tags.

Zum Hauptmenü