# Das große MySQL-Tutorial für Einsteiger

Die Grundlage unseres digitalen Universums ist ein stetig wachsender Datenberg. Zentrales Element des Internets und der vernetzten Welt sind Datenbank-Management-Systeme wie [MySQL](https://www.ionos.at/digitalguide/server/knowhow/was-ist-mysql/ "Was ist MySQL?"). Diese ermöglichen es, große Datenmengen elektronisch zu verarbeiten, widerspruchsfrei abzulegen und dauerhaft aufzubewahren. Dabei werden komplexe Datenbestände in handliche Teilmengen zerlegt und bei Bedarf in Relation gesetzt. In unserem MySQL-Tutorial für Anfänger führen wir Sie in die Grundlagen der Datenbankverwaltung ein und zeigen Ihnen an Beispielen, wie Sie die Datenhaltung für Ihr Webprojekt mit MySQL optimieren.

## Was ist MySQL?

MySQL gehört neben Oracle und Microsoft SQL Server zu den weltweit populärsten **relationalen Datenbank-Management-Systemen** (eine aktuelle Übersicht finden Sie auf [db-engines.com](http://db-engines.com/de/ranking "Popularität von Datenbank-Management-Systemen im DB-Engines-Ranking")). Die 1994 vom schwedischen Unternehmen MySQL AB entwickelte Software steht heute unter der Schirmherrschaft der **Oracle Corporation** und wird auf Basis eines **dualen Lizenzsystems** vertrieben: Neben der proprietären Enterprise-Ausführung bietet Oracle eine GPL-lizenzierte, quelloffene Version an. Diese Doppellizenzierung gibt Unternehmen die Möglichkeit, eigene Anwendungen auf Basis von MySQL zu entwickeln, ohne diese der Open-Source-Lizenz unterwerfen zu müssen. In der Open-Source-Gemeinde stößt die Übernahme von MySQL durch Oracle jedoch überwiegend auf Kritik.

---

### Tipp

Datenbank Hosting gesucht? Hier finden Sie professionelles [SQL-Hosting](https://www.ionos.at/cloud/sql-server-hosting "Professionelles SQL-Hosting") mit dem Datenbank-Management-System Ihrer Wahl.

---

MySQL ist in C und C++ geschrieben und verfügt über einen Yacc-basierten SQL-Parser mit selbst entwickeltem Tokenizer (lexikalischer Scanner). Das Datenbank-Management-System zeichnet sich zudem durch eine breite Betriebssystem-Unterstützung aus.

---

### Fakt

Die Abkürzung SQL steht für „Structured Query Language“, eine Computersprache, die der Verwaltung von Datenbankstrukturen dient. Mögliche Operationen umfassen das Abfragen, Einfügen, Aktualisieren und Löschen von Datenbeständen.

---

### MariaDB – MySQL-Fork mit Potenzial

Die Eingliederung des MySQL-Projekts in das Oracle-Produktportfolio stößt in der Entwicklerszene überwiegend auf Misstrauen und Kritik. Dies hat in erster Linie damit zu tun, dass die Unterschiede zwischen der GPL-lizenzierten [MySQL-Version](https://www.ionos.at/digitalguide/hosting/hosting-technik/mysql-version/ "MySQL-Version") und dem kostenpflichtigen Enterprise-Produkt stetig zunehmen. Neue Funktionen des Datenbank-Management-Systems stehen immer häufiger nur in der proprietären Version zur Verfügung. **Nichtöffentliche Fehler-Datenbanken** und mangelnde Tests lassen die Community vermuten, dass das Open-Source-Projekt unter den Fittichen des Software-Goliaths Oracle nur stiefmütterlich behandelt wird. Der Rückhalt in der Open-Source-Gemeinschaft nimmt daher stetig ab.

Bereits 2009 hat das Kernentwicklerteam um den MySQL-Erfinder Michael „Monty“ Widenius dem beliebten Datenbanksystem den Rücken gekehrt und **mit MariaDB einen** **quelloffenen Fork** der Software initiiert. Ende 2012 leiteten mit Fedora, OpenSUSE, Slackware und Arch Linux die ersten Linux-Distributionen die Umstellung von MySQL auf MariaDB als Standard-Installation ein. Zahlreiche Open-Source-Projekte sowie namhafte Software-Firmen und Webplattformen folgten dem Beispiel – darunter Mozilla, Ubuntu, Google, Red Hat Enterprise Linux, Web of Trust, Team Speak, die Wikimedia Foundation und das bereits angesprochene Software-Projekt XAMPP.

Schon jetzt zeichnet sich ab, dass sich MariaDB im Vergleich zur quelloffenen MySQL-Version kontinuierlicher weiterentwickelt. Es ist daher anzunehmen, dass der Fork seinem Mutterprojekt bald den Rang ablaufen wird.

---

### Tipp

Als Fork wird in der Software-Branche ein Entwicklungszweig bezeichnet, der durch Abspaltung von einem (meist quelloffenen) Projekt entsteht. Ein Fork baut auf dem Quellcode des Mutterprojekts auf und entwickelt diesen in einem unabhängigen Folgeprojekt weiter.

---

### Datenbanksysteme

Die elektronische Datenverwaltung findet heute größtenteils in **Datenbanksystemen (DBS)** statt. Diese bestehen grundsätzlich aus zwei Komponenten: der **Datenbank (DB)** selbst und dem zu ihrer Verwaltung nötigen [Datenbank-Management-System (DBMS)](https://www.ionos.at/digitalguide/hosting/hosting-technik/datenbankmanagementsystem-dbms-erklaert/ "Datenbankmanagementsystem (DBMS) erklärt").

- **Das Datenbank-Management-System:** Beim DBMS handelt es sich um eine Software wie MySQL, die der Verwaltung des DBS dient. Zu den Aufgaben dieser Verwaltungssoftware gehört die Strukturierung der Daten gemäß einem vordefinierten Datenbankmodell. Darüber hinaus kontrolliert das DBMS Schreib- und Lesezugriffe auf die Datenbank, verwaltet große Datenmengen sowie parallele Datenbankzugriffe und sorgt dafür, dass Richtlinien zu Datenintegrität, Datenschutz und Datensicherheit eingehalten werden.
- **Die Datenbank:** Eine Datenbank ist ein inhaltlich zusammenhängender Datenbestand, z. B. Kundendaten oder die CMS-Daten. Ein DBMS kann mehrere DBs zugleich verwalten.

Folgende Grafik zeigt eine schematische Darstellung eines Datenbanksystems:

[![Bild: Schematische Darstellung eines Datenbanksystems (DBS) mit drei Datenbanken](https://www.ionos.at/digitalguide/fileadmin/_processed_/7/e/csm_schematische-darstellung-eines-datenbanksystems_e1d2628fdb.webp "Schematische Darstellung eines Datenbanksystems (DBS) mit drei Datenbanken")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/schematische-darstellung-eines-datenbanksystems.png) Ein Datenbanksystem (DBS) besteht aus einem Datenbank-Management-System (DBMS) und beliebig vielen Datenbanken (DB).       ###  Das relationale Datenbankmodell

MySQL ist der Definition nach ein **relationales DBMS**. Das bedeutet, dass die Datenverwaltung auf einem tabellenbasierten Datenbankmodell beruht. Sämtliche Daten, die MySQL verarbeitet, werden in Tabellen gespeichert, die sich durch Schlüssel (keys) in Beziehung setzen lassen.

Wir veranschaulichen dies an einem einfachen Beispiel. Nachstehend finde Sie die beiden Tabellen *autoren* und *werke*:

[![Bild: Datenbankabfrage](https://www.ionos.at/digitalguide/fileadmin/_processed_/8/e/csm_abfrageergebnis-datenbankabfrage_0cfdc7bf87.webp "Datenbankabfrage")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/abfrageergebnis-datenbankabfrage.png)      Alle Tabellen einer relationalen Datenbank bestehen aus Spalten und Zeilen. Jede **Spalte** einer Tabelle steht für ein bestimmtes **Attribut**. In der Tabelle *autoren* finden sich beispielsweise die Attribute *id*, *vorname* und *nachname*. Die Zeilen einer Tabelle werden als **Rows** bezeichnet und beinhalten je einen **Datensatz**. Dieser wird in der Regel durch einen **Primärschlüssel** eindeutig identifiziert (nummeriert). Welches Attribut als Primarschlüssel fungiert, wird bei der Erstellung der Tabelle definiert. Voraussetzung ist, dass der Primärschlüssel eine eindeutige Zuordnung ermöglicht. Dazu darf jeder Primärschlüssel innerhalb einer Spalte nur einmal verwendet werden. Es empfiehlt sich eine Durchnummerierung via **ID**.

Die Tabelle werke führt neben dem Primärschlüssel werke\_id zudem die autoren\_id als Fremdschlüssel (foreign key) auf. Dieser stellt eine Beziehung zwischen beiden Tabellen her und ermöglicht es, die Datensätze der einen Tabelle mit denen der anderen zu verknüpfen. Setzt man zwei Tabellen einer relationalen Datenbank in Verbindung, spricht man von einem Join. Ein solcher lässt sich beispielsweise durch folgende Datenbankabfrage realisieren: „Rufe alle Werke des Autors John Ronald Reuel Tolkien inklusive Datum der Erstveröffentlichung ab“.

[![Bild: Ergebnis Datenbankabfrage](https://www.ionos.at/digitalguide/fileadmin/_processed_/9/5/csm_abfrageergebnis_e0b70cff08.webp "Ergebnis Datenbankabfrage")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/abfrageergebnis.png)      Tolkien ist in der Tabelle *autoren* mit dem Primärschlüssel *autoren\_id* *1* gelistet. Um sämtliche Werke des Autors abzufragen, kommt dieser in der Tabelle *werke* als Fremdschlüssel zum Einsatz. Es werden somit alle Rows abgerufen, die mit der *autoren\_id 1* verknüpft sind.

In der Praxis werden MySQL-Datenbankoperationen mit standardisierten **SQL-Befehlen** wie SELECT, INSERT, UPDATE und DELETE realisiert. 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 Datenhaltung führt jedoch dazu, dass eine Datenbank eine Vielzahl redundanter Einträge beinhaltet, da beispielsweise die Daten zu den Spalten *vorname* und *nachname* in diesem Fall für jedes Werk einzeln aufgeführt werden müssten. Eine solche Redundanz belastet nicht nur den Speicher, sondern führt auch dazu, dass etwaige Aktualisierungen an mehreren Stellen der Datenbank vorgenommen werden müssen. Bei der Arbeit mit relationalen Datenbanksystemen beschränkt man sich daher darauf, nur einen Sachverhalt pro Tabelle abzubilden. Man spricht in diesem Fall von einer **Normalisierung** der Daten.

Das zentrale Anwendungsfeld von MySQL ist die **Datenspeicherung im Rahmen dynamischer Webangebote**. Die Kombination von MySQL mit der Webserver-Software Apache und den [Skriptsprachen](https://www.ionos.at/digitalguide/websites/web-entwicklung/was-sind-skriptsprachen/ "Was sind Skriptsprachen?") PHP oder Perl hat sich als klassisches Software-Gerüst in der Webentwicklung etabliert. Der Web-Stack lässt sich als [LAMP](https://www.ionos.at/digitalguide/server/knowhow/lamp-server-die-loesung-fuer-dynamische-websites/ "LAMP-Server – die Lösung für dynamische Websites") (Linux), [MAMP](https://www.ionos.at/digitalguide/server/knowhow/mamp-der-praktische-software-stack-fuer-mac-os-x/ "MAMP: Der praktische Software-Stack für Mac OS X") (macOS) oder WAMP (Windows) mit allen gängigen Server-Betriebssystemen realisieren.

MySQL-Einsteigern empfehlen wir die lokale Textumgebung XAMPP, um erste Erfahrungen mit dem Datenbank-Verwaltungssystem zu sammeln. Dieses setzt in der aktuellen Version auf MariaDB.

## Installation des Datenbank-Management-Systems

Um Ihnen die MySQL-Grundlagen näher zu bringen, werden wir mit praxisnahen Beispielen arbeiten. Unserer MySQL-Einführung liegt dabei die **Testumgebung XAMPP** zugrunde. Code-Snippets und Screenshots konzentrieren sich auf Datenbankoperationen, die via **PHP** mithilfe **eines Apache HTTP Servers** lokal auf einem Windows-Rechner realisiert wurden. Statt der klassischen MySQL-Datenbank kommt dabei der Fork MariaDB zum Einsatz. Derzeit sind beide Datenbank-Management-Systeme so kompatibel, dass sich alle Operationen 1:1 übertragen lassen. Für Sie als Endanwender macht es im Rahmen eines Einsteiger-Tutorials keinen Unterschied, ob Sie mit MySQL oder MariaDB arbeiten. Wie Sie eine solche Testumgebung lokal auf Ihrem Windows-Rechner installieren, ist Thema unseres [**XAMPP-Tutorials**](https://www.ionos.at/digitalguide/server/tools/xampp-tutorial-so-erstellen-sie-ihren-lokalen-testserver/). Möchten Sie den Umgang mit relationalen Datenbanken von der Pike auf lernen, empfehlen wir, direkt mit MariaDB einzusteigen. Eine alternative Testumgebung auf Basis von MySQL steht Ihnen mit **[AMPPS](http://www.ampps.com/ "Mehr zu AMPPS auf der offiziellen Projekt-Website")** jedoch ebenfalls kostenlos zur Verfügung. Darüber hinaus besteht die Möglichkeit, einen benutzerdefinierten Web-Stack aufzusetzen. MySQL und MariaDB lassen sich bei Bedarf mit diversen Betriebssystemen, Webservern und Skriptsprachen kombinieren. Kostenlose, GPL-lizenzierte Downloadpakete werden unter mysql.de und mariadb.com angeboten. Detaillierte Installationsanleitungen für verschiedene Plattformen finden Sie in den englischsprachigen Dokumentationen von MySQL und MariaDB.

## Datenbankverwaltung mit phpMyAdmin

Bei der Verwaltung von MySQL stützen wir uns auf die freie Webanwendung **phpMyAdmin**. Diese ist bereits im Installationsumfang des XAMPP-Software-Bundles enthalten, wird auf der offiziellen Projekt-Website jedoch auch separat als [Download-Paket](https://www.phpmyadmin.net/ "phpMyAdmin | Kostenloser Download auf der Projektseite") angeboten. phpMyAdmin gilt als Standardsoftware zur Administration von MySQL-Datenbanken im World Wide Web. Die in PHP und JavaScript geschriebene Webanwendung ermöglicht es, Datenbankoperationen über eine grafische Benutzeroberfläche durchzuführen. So erstellen und verwalten Sie die Tabellen Ihrer relationalen Datenbank bequem per Klick im Webbrowser. Die Kenntnis entsprechender SQL-Befehle ist dazu zunächst nicht notwendig.

### phpMyAdmin aufrufen

Wurde das Software-Bundle XAMPP installiert, **starten Sie das Datenbank-Management-System (MySQL oder MariaDB)** analog zu den anderen Komponenten des Test-Stacks über das Control-Panel. Nutzen Sie dazu die unter „Actions“ aufgeführte Schaltfläche „Start“. Um phpMyAdmin über den Webbrowser aufrufen zu können, müssen Sie zudem den Webserver Apache starten. Aktivierte Module werden im **XAMPP-Control-Panel** grün hinterlegt. Zusätzlich bekommen Sie den aktuellen Status der XAMPP-Module als Meldung im Textfenster ausgegeben.

[![Bild: Das XAMPP-Control-Panel](https://www.ionos.at/digitalguide/fileadmin/_processed_/e/d/csm_xampp-control-panel-fuer-php-myadmin_a4b9bf8be9.webp "Das XAMPP-Control-Panel")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/xampp-control-panel-fuer-php-myadmin.png) Starten Sie MySQL sowie den Apache HTTP Server über das XAMPP-Control-Panel       ---

### Fakt

XAMPP wurde im Rahmen des Software-Projekts Apache Friends als kompaktes Testsystem für den Einsatz auf einem lokalen Rechner entwickelt. Das Software-Bundle ist nicht dazu gedacht, Webdienste im Internet bereitzustellen. Als Produktivsystem ist XAMPP aufgrund zahlreicher Einschränkungen im Bereich der Sicherheit nicht geeignet.

---

Im lokalen Testbetrieb steht Ihnen die **Weboberfläche der Administrationssoftware** unter "*http://localhost/phpmyadmin/"* zur Verfügung.

Sofern Sie bei der MySQL-Installation ein **Passwort** für den *root*-Account definiert haben, fragt phpMyAdmin dieses über einen Log-in-Bildschirm ab. Nutzen Sie phpMyAdmin im Rahmen eines Webhosting-Produkts, werden entsprechende Anmeldedaten über den jeweiligen Anbieter vergeben. In der Regel besitzen Sie in diesem Fall keine *root*-Rechte.

Nach erfolgreichem Log-in präsentiert phpMyAdmin Ihnen die Startseite der Anwendung. Diese bietet Ihnen die Möglichkeit, grundlegende Einstellungen zum Zeichensatz (Kollation) der MySQL-Verbindung zu treffen sowie den gewünschten Anzeigemodus (Sprache, Design und Schriftgröße) zu wählen. Zudem finden Sie auf der rechten Seite eine Übersicht der Eckdaten Ihres Datenbank-Servers, der verwendeten Webserver-Software sowie Informationen über die aktuelle Version von phpMyAdmin. Die Menüleiste der Startseite ist wie alle anderen Menüleisten der Anwendung in Form von Registerkarten gestaltet. Zur Auswahl stehen die Tabs Databases, SQL, Status, User accounts, Export, Import, Settings, Replication, Variables und More.

[![Bild: Startseite der MySQL-Administrationssoftware phpMyAdmin](https://www.ionos.at/digitalguide/fileadmin/_processed_/a/7/csm_startseite-mysql-administrationssoftware-phpmyadmin_39772007b7.webp "Startseite der MySQL-Administrationssoftware phpMyAdmin")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/startseite-mysql-administrationssoftware-phpmyadmin.png) Nach dem Aufruf der URL http://localhost/phpmyadmin/ präsentiert phpMyAdmin die Startseite der Webanwendung       Am linken Rand der Benutzeroberfläche finden Sie ein **Navigations-Panel**. Hier werden alle Tabellen aufgeführt, auf die Sie mithilfe von phpMyAdmin in Ihrer Datenbank zugreifen können. Unter dem Programm-Logo in der linken oberen Ecke hält die Software Links zur Startseite sowie zu offiziellen Dokumentationen bereit. Darüber hinaus haben Sie die Möglichkeit, das Navigations-Panel zu konfigurieren und Ihre Ansicht zu aktualisieren.

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

### Datenbank anlegen

Um eine Datenbank mit phpMyAdmin anzulegen, wählen zunächst den Tab „Databases“ (Datenbanken) in der Menüleiste der Startseite aus.

[![Bild: phpMyAdmin: Tab „Databases“](https://www.ionos.at/digitalguide/fileadmin/_processed_/9/5/csm_phpmyadmin-tab-databases_d8235ae674.webp "phpMyAdmin: Tab „Databases“")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-tab-databases.png) Datenbanken legen Sie mit phpMyAdmin im Tab „Databases“ an       Geben Sie einen gewünschten **Namen für Ihre Datenbank** in das Eingabefeld unter „Create database“ (Neue Datenbank anlegen) ein und wählen Sie einen Zeichensatz. Wir empfehlen die Kollation *utf8mb4\_unicode\_ci*. Mit der Wahl eines Zeichensatzes teilen Sie dem Datenbankserver mit, welche Codierung für die zu sendenden bzw. empfangenden Daten verwendet werden soll. Die mb4-Varianten erlauben auch exotische Zeichen wie Symbole oder Emojis, die außerhalb der Unicode-Basis-Ebene (Basic Multilingual Plane) liegen, und sind deshalb zu empfehlen.

[![Bild: Datenbank erstellen: Namen und Zeichensatz definieren](https://www.ionos.at/digitalguide/fileadmin/_processed_/8/1/csm_datenbank-erstellen_a00c3e158b.webp "Datenbank erstellen: Namen und Zeichensatz definieren")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/datenbank-erstellen.png) Um eine Datenbank zu erstellen, wählen Sie einen Namen und bestimmen den zugrundeliegenden Zeichensatz       Bestätigen Sie Ihre Eingabe mit einem Klick auf „Create“ (Anlegen). Die von Ihnen erstellte Datenbank wird im Navigations-Panel auf der linken Bildschirmseite angezeigt. Neu erstellte Datenbanken enthalten zunächst keine Inhalte. Um Daten zu hinterlegen, erstellen Sie im nächsten Schritt eine Tabelle.

## Tabellen anlegen

Um eine neue Tabelle anzulegen, wählen Sie die gewünschte Datenbank aus und navigieren über die Menüleiste in den Tab „Structure“ (Struktur).

[![Bild: Tabellen anlegen mit phpMyAdmin](https://www.ionos.at/digitalguide/fileadmin/_processed_/7/6/csm_tabellen-anlegen-mit-phpmyadmin_d9b411dddc.webp "Tabellen anlegen mit phpMyAdmin")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/tabellen-anlegen-mit-phpmyadmin.png) Über Create table legen Sie eine neue Datentabelle an       Legen Sie eine Tabelle an, indem Sie einen Namen (z. B. *user*s) sowie die **gewünschte Anzahl an Spalten** in die Schaltfläche „Create table“ (Erzeuge Tabelle) eingeben. Sie erinnern sich: Jede Spalte steht für ein Attribut eines Datensatzes. Benötigen Sie zusätzliche Spalten, können Sie diese auch nachträglich hinzufügen.

Möchten Sie beispielsweise eine Nutzerdatenbank für Ihre Website anlegen, bieten sich folgende Bezeichnungen für die Spalten der Tabelle an:

[![Bild: Tabelle](https://www.ionos.at/digitalguide/fileadmin/_processed_/0/9/csm_beispiel-tabelle_87a635c9df.webp "Tabelle")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/beispiel-tabelle.png)      Für Ihre Nutzerdatenbank erstellen Sie somit eine Tabelle *users* mit sieben Spalten. Ihre Eingabe bestätigen Sie mit „Go“.

[![Bild: Tabelle anlegen via „Create table“](https://www.ionos.at/digitalguide/fileadmin/_processed_/6/1/csm_tabelle-anlegen-via-create-table_74206db869.webp "Tabelle anlegen via „Create table“")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/tabelle-anlegen-via-create-table.png) Definieren Sie einen Tabellennamen und die gewünschte Anzahl an Spalten       Nachdem die Tabelle angelegt wurde, gibt phpMyAdmin Ihnen die Möglichkeit, Bezeichnungen für die Tabellenspalten zu definieren und **Formateinstellungen für die zu erwartenden Daten** zu treffen.

[![Bild: Die Beispieltabelle users mit sieben Spalten](https://www.ionos.at/digitalguide/fileadmin/_processed_/6/2/csm_beispieltabelle-users_8942fe0784.webp "Die Beispieltabelle users mit sieben Spalten")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/beispieltabelle-users.png) Die Beispieltabelle users – zunächst ohne Attribute       Eine Beschreibung der Tabellenstruktur und möglicher Formatierungen finden Sie in folgender Tabelle.

<table>
  <tbody>
    <tr>
      <td><strong>Option</strong></td>
      <td><strong>Beschreibung</strong></td>
    </tr>
    <tr>
      <td>Name</td>
      <td>Jede Spalte einer Datenbanktabelle wird mit einem Namen versehen. Dieser kann mit Einschränkungen frei gewählt werden. Unproblematisch sind lateinische Buchstaben (groß und klein, aber ohne Umlaute), Ziffern, Dollarzeichen und Unterstrich. Diesen können Sie als Alternative zum nicht erlaubten Leerzeichen verwenden (falsch: *user id*; richtig: *user\_id*). Spaltennamen dürfen nicht nur aus Ziffern bestehen. Zudem finden sich in der Datenbanksprache SQL diverse Keywords, die für bestimme Aufgaben reserviert sind. Eine Liste lässt sich der [MySQL-Dokumentation](https://dev.mysql.com/doc/refman/5.6/en/keywords.html "Liste reservierter Keywords auf mysql.com") entnehmen. Die meisten dieser Einschränkungen 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](http://dev.mysql.com/doc/refman/5.7/en/identifiers.html "Namen für MySQL-Spalten und -Tabellen"). Zu empfehlen sind sprechende Spaltennamen passend zum jeweiligen Attribut.</td>
    </tr>
    <tr>
      <td>Type</td>
      <td>Der Datentyp gibt an, welche Art von Daten in einer Spalte gespeichert wird. MySQL und MariaDB ermöglichen Ihnen, Daten als Ganz- und Fließkommazahlen, Zeit- und Datumsangaben sowie Textstrings und Binärdaten zu definieren. Eine Beschreibung finden Sie in der Tabelle Datentypen.</td>
    </tr>
    <tr>
      <td>Length/Values</td>
      <td>Bei manchen Datentypen (zum Beispiel Textstrings) können Sie den Werten einer Spalte eine Maximallänge zuweisen. Diese Formatierung ist optional.</td>
    </tr>
    <tr>
      <td>Default</td>
      <td>Die Option „Default“ erlaubt Ihnen, einen Standardwert für eine Spalte zu definieren. Dieser wird immer dann automatisch eingefügt, wenn ein Datensatz keinen Wert für die jeweilige Spalte enthält.</td>
    </tr>
    <tr>
      <td>Collation</td>
      <td>Mit der Option „Collation“ definieren Sie für eine Spalte einen bestimmten Zeichentyp, der von den globalen Datenbankeinstellungen abweichen kann. Sie können die Kodierung auch auf Tabellenebene für alle Spalten ändern.</td>
    </tr>
    <tr>
      <td>Attributes</td>
      <td>Manche Datentypen lassen sich durch optionale Attribute näher bestimmen. So können Sie beispielsweise mit den Attributen *signed* und *unsigned* festlegen, ob eine Ganz- oder Fließkommazahl auch negative (*signed*) oder nur positive (*unsigned*) Werte annehmen kann.</td>
    </tr>
    <tr>
      <td>Index</td>
      <td>Über die Option „Index“ definieren Sie Regeln für die Indexierung. Wählen Sie für eine Spalte die Index-Einstellung *PRIMARY*, gilt diese als Primärschlüssel der Tabelle. Die Einstellung *UNIQUE* legt fest, dass Werte innerhalb dieser Spalte nur einmal gespeichert werden können. So lassen sich Doppelungen bei Bedarf vermeiden.</td>
    </tr>
    <tr>
      <td>A\_I</td>
      <td>Die Abkürzung „A\_I“ steht für *AUTO\_INCREMENT* und weist das Datenbank-Management-System an, einen Wert automatisch hochzuzählen, wenn beim Anlegen eines Datensatzes keiner angegeben wurde. Zum Einsatz kommt diese Option bei der Indexierung von Datensätzen.</td>
    </tr>
    <tr>
      <td>Comments</td>
      <td>Das Feld „Comments“ ermöglicht Ihnen, Tabellenspalten mit Kommentaren zu versehen.</td>
    </tr>
  </tbody>
</table>

Die hier dargestellten Optionen umfassen die wichtigsten Einstellungen zur Formatierung von Tabellenspalten. Wenn Sie mithilfe der Bildlaufleiste weiter nach rechts scrollen, finden Sie weitere Einstellungsmöglichkeiten, die wir in diesem MySQL-Tutorial für Einsteiger nicht ansprechen.

Nachstehende Tabelle führt **verschiedene Datentypen**, die sich mit MySQL und MariaDB verarbeiten lassen, sowie deren Wertebereich und Speicherbedarf auf.

<table>
  <tbody>
    <tr>
      <td><strong>Typ</strong></td>
      <td><strong>Beschreibung</strong></td>
      <td><strong>Wertebereich</strong></td>
      <td><strong>Speicherbedarf</strong></td>
    </tr>
    <tr>
      <td>TINYINT</td>
      <td>Eine sehr kleine Ganzzahl</td>
      <td>Ohne Vorzeichen: 0 bis 255 Mit Vorzeichen: -128 bis +127</td>
      <td>1 Byte</td>
    </tr>
    <tr>
      <td>SMALLINT</td>
      <td>Eine kleine Ganzzahl</td>
      <td>Ohne Vorzeichen: 0 bis 65.535 Mit Vorzeichen: -32.768 bis +32.767</td>
      <td>2 Byte</td>
    </tr>
    <tr>
      <td>MEDIUMINT</td>
      <td>Eine Ganzzahl mittlerer Größe</td>
      <td>Ohne Vorzeichen: 0 bis 16.777.215 Mit Vorzeichen: -8.388.608 bis +8.388.607</td>
      <td>3 Byte</td>
    </tr>
    <tr>
      <td>INT/INTEGER</td>
      <td>Eine Ganzzahl normaler Größe</td>
      <td>Ohne Vorzeichen: 0 bis 4.294.967.295 Mit Vorzeichen: -2.147.483.648 bis +2.147.483.647</td>
      <td>4 Byte</td>
    </tr>
    <tr>
      <td>BIGINT</td>
      <td>Eine große Ganzzahl</td>
      <td>Ohne Vorzeichen: 0 bis 18.446.744.073.709.551.615 Mit Vorzeichen: -9.223.372.036.854.775.808 bis +9.223.372.036.854.775.807</td>
      <td>8 Byte</td>
    </tr>
    <tr>
      <td>FLOAT</td>
      <td>Eine Fließkommazahl mit einfacher Genauigkeit</td>
      <td>Ohne Vorzeichen: 0 bis 3,4e+38 Mit Vorzeichen: -3,4e+38 bis 3,4e+38</td>
      <td>4 Byte</td>
    </tr>
    <tr>
      <td>DOUBLE</td>
      <td>Eine Fließkommazahl mit doppelter Genauigkeit</td>
      <td>Ohne Vorzeichen: 0 bis 3,4e+38 Mit Vorzeichen: -3,4e+38 bis 3,4e+38</td>
      <td>8 Byte</td>
    </tr>
    <tr>
      <td>DATE</td>
      <td>Datum im Format 'YYYY-MM-DD'</td>
      <td>'1000-01-01' bis '9999-12-31'</td>
      <td>3 Byte</td>
    </tr>
    <tr>
      <td>TIME</td>
      <td>Zeitangabe im Format 'HH:MM:SS.ssssss'</td>
      <td>'-838:59:59.999999' bis '838:59:59.999999'</td>
      <td>3 Byte</td>
    </tr>
    <tr>
      <td>DATETIME</td>
      <td>Datumsangabe im Format 'YYYY-MM-DD HH:MM:SS.ssssss'</td>
      <td>Entspricht DATE und TIME (bis 23:59:59.999999 Stunden)</td>
      <td>8 Byte</td>
    </tr>
    <tr>
      <td>TIMESTAMP</td>
      <td>Zeitstempel im Format 'YYYY-MM-DD HH:MM:DD'</td>
      <td>'1970-01-01 00:00:01' (UTC) bis '2038-01-19 05:14:07' (UTC)</td>
      <td>4 Byte</td>
    </tr>
    <tr>
      <td>YEAR</td>
      <td>Jahr zwischen 1901 bis 2155</td>
      <td>1901 bis 2155 und 0000</td>
      <td>1 Byte</td>
    </tr>
    <tr>
      <td>CHAR</td>
      <td>Zeichenkette fester Länge; Anzahl der Zeichen entspricht M</td>
      <td>Für M: 0 bis 255 Zeichen</td>
      <td>M Byte</td>
    </tr>
    <tr>
      <td>VARCHAR</td>
      <td>Zeichenkette variabler Länge; Anzahl der Zeichen entspricht M</td>
      <td>Für M: 0 bis 65.535 Zeichen</td>
      <td>Max. M + 2 Byte</td>
    </tr>
    <tr>
      <td>TINYTEXT</td>
      <td>Sehr kleine Zeichenkette variabler Länge; Anzahl der Zeichen entspricht M</td>
      <td>Für M: 0 bis 255 Zeichen</td>
      <td>M + 1 Byte</td>
    </tr>
    <tr>
      <td>TEXT</td>
      <td>Zeichenkette variabler Länge; Anzahl der Zeichen entspricht M</td>
      <td>Für M: 0 bis 65.535 Zeichen</td>
      <td>M + 2 Byte</td>
    </tr>
    <tr>
      <td>MEDIUMTEXT</td>
      <td>Mittelgroße Zeichenkette variabler Länge; Anzahl der Zeichen entspricht M</td>
      <td>Für M: 0 bis 16.777.215 Zeichen</td>
      <td>M + 3 Byte</td>
    </tr>
    <tr>
      <td>LONGTEXT</td>
      <td>Lange Zeichenkette variabler Länge; Anzahl der Zeichen entspricht M</td>
      <td>Für M: 0 bis 4.294.967.295 Zeichen (4 GB)</td>
      <td>M + 4 Byte</td>
    </tr>
    <tr>
      <td>BLOB</td>
      <td>Bei einem [BLOB (Binary Large Object)](https://www.ionos.at/digitalguide/websites/web-entwicklung/binary-large-object-blob/) handelt es sich um ein binäres Objekt mit Daten variabler Länge (z. B. Bilder, Audiodaten).</td>
      <td>Max. Länge M: 65.535 Byte</td>
      <td>M + 2 Byte</td>
    </tr>
    <tr>
      <td>TINYBLOB</td>
      <td>Kleines binäres Objekt mit Daten variabler Länge</td>
      <td>Max. Länge M: 255 Byte</td>
      <td>M + 1 Byte</td>
    </tr>
    <tr>
      <td>MEDIUMBLOB</td>
      <td>Mittleres binäres Objekt mit Daten variabler Länge.</td>
      <td>Max. Länge M: 16.777.215 Byte</td>
      <td>M + 3 Byte</td>
    </tr>
    <tr>
      <td>LONGBLOB</td>
      <td>Großes binäres Objekt mit Daten variabler Länge.</td>
      <td>Max. Länge M: 4.294.967.295 Byte (4 GB).</td>
      <td>M + 4 Byte</td>
    </tr>
    <tr>
      <td>ENUM (Aufzählung)</td>
      <td>Ein String-Objekt, dessen zulässige Werte bei der Erstellung der Spalte definiert werden</td>
      <td>Max. 65,535 unterschiedliche Elemente</td>
      <td>1 oder 2 Bytes, abhängig von der Anzahl der möglichen Werte</td>
    </tr>
    <tr>
      <td>SET</td>
      <td>Ein String-Objekt, dessen zulässige Werte bei der Tabellenerstellung definiert werden. Mehrfachauswahl ist möglich</td>
      <td>Max. 64 unterschiedliche Werte</td>
      <td>1, 2, 3, 4, oder 8, abhängig von der Anzahl der möglichen Werte</td>
    </tr>
  </tbody>
</table>

Für die Beispieltabelle *users* haben wir folgende Einstellungen 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 Zahlenwerte annehmen. Unter „Index“ haben wir für die *id* die Einstellung *PRIMARY* ausgewählt. Die Identifikationsnummer fungiert somit als **Primärschlüssel** für die Tabelle *users*. Das Häkchen bei „**A\_I**“ (*Auto\_Increment*) signalisiert dem Datenbank-Management-System, dass IDs für jeden Eintrag als fortlaufende Nummern automatisch 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 Zeichenketten, deren Länge (M) wir mit der Option „**Length/Value**“ auf 50 Zeichen eingeschrä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 gespeichert wird.

Für die Spalten created\_at und updated\_at haben wir den Datentyp TIMESTAMP gewählt. Das Datenbank-Management-System speichert Zeitdaten zur Erstellung und Aktualisierung von Datensätzen somit im Format YYYY-MM-DD HH:MM:DD. Da das System für jeden neuen Eintrag automatisch einen Zeitstempel erstellen soll, wählen wir für die Spalte created\_at den Standardwert CURRENT\_TIMESTAMP. Die Spalte updated\_at wird erst dann relevant, wenn wir einen Eintrag aktualisieren. Wir erlauben daher Null-Werte für diese Spalte und setzten NULL als Standardwert.

---

### Fakt

Der Wert *NULL* repräsentiert in PHP einen leeren Eintrag. Ein Datenfeld hat den Wert *NULL*, wenn diesem noch kein Wert zugewiesen wurde.

---

[![Bild: Formatierung der Tabellenspalten](https://www.ionos.at/digitalguide/fileadmin/_processed_/0/d/csm_formatierung-der-tabellenspalten_19eb0e38fc.webp "Formatierung der Tabellenspalten")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/formatierung-der-tabellenspalten.png) Die Beispieltabelle users nach der Formatierung     Als **Storage-Engine** verwenden wir das MySQL-Standard-Tabellenformat [InnoDB](https://www.ionos.at/digitalguide/hosting/hosting-technik/was-ist-innodb/ "Was ist InnoDB?")*.*

Alle Tabelleneinstellungen, die Sie über die grafische Benutzeroberfläche treffen, werden von phpMyAdmin in SQL-Code übersetzt. Diesen lassen Sie sich bei Bedarf über die Schaltfläche „**SQL-Vorschau**“ ausgeben.

```none
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 detaillierte Beschreibung der SQL-Syntax folgt im Kapitel zu Datenbankabfragen.

Ein Klick auf „Save“ speichert Ihre Einstellungen. Die Tabelle *users* wird im Navigations-Panel unter der Datenbank *test* angezeigt.

### Tabellen verwalten

Um eine erstellte Tabelle zu verwalten, klicken Sie auf den Tabellennamen im Navigations-Panel. phpMyAdmin zeigt Ihnen im Tab „Browse“ (Anzeige) eine Übersicht der in der jeweiligen Tabelle gespeicherten Daten. Im Fall unserer Beispieltabelle liefert die Abfrage ein leeres Resultat. Es befinden sich noch keine Datensätze in der Tabelle *users*.

[![Bild: phpMyAdmin: Tabellenübersicht](https://www.ionos.at/digitalguide/fileadmin/_processed_/5/3/csm_phpmyadmin-tabellenuebersicht_d29538a40a.webp "phpMyAdmin: Tabellenübersicht")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-tabellenuebersicht.png) Im Tab „Browse“ zeigt phpMyAdmin eine Übersicht der in der Tabelle gespeicherten Daten an       Zur **Verwaltung von Datentabellen** stehen Ihnen in der Menüleiste verschiedene Tabs zur Verfügung. Möchten Sie die Struktur einer Datentabelle verändern, wählen Sie den Tab „Structure“. Neue Datensätze fügen Sie Ihrer Tabelle im Tab „Insert“ (Einfügen) hinzu. Darüber hinaus ermöglicht es phpMyAdmin, Tabellen zu durchsuchen, Berechtigungen zu verwalten und Datensätze zu exportieren oder aus anderen Tabellen zu importieren.

#### Tabellenstruktur überarbeiten

Möchten Sie Ihre Tabelle nachträglich um zusätzliche Spalten ergänzen, existierende löschen oder überarbeiten, wechseln Sie in den Reiter „Structure“*.*

**Ergänzen Sie Spalten** über die Schaltfläche „Add columns“, indem Sie die gewünschte Anzahl neuer Spalten sowie die jeweilige Position angeben.

In folgender Grafik wird die neue Spalte nach *updated\_at* eingefügt:

[![Bild: phpMyAdmin-Schaltfläche: Add columns](https://www.ionos.at/digitalguide/fileadmin/_processed_/5/3/csm_phpmyadmin-schaltflaeche-add-columns_0143962da0.webp "phpMyAdmin-Schaltfläche: Add columns")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-schaltflaeche-add-columns.png) Über die Schaltfläche „Add columns“ geben Sie an, wie viele Spalten Sie wo einfügen möchten       Möchten Sie bereits **bestehende Spalten löschen**, wählen Sie diese über ein Häkchen in der Check-Box aus und klicken anschließend auf „Drop“.

[![Bild: phpMyAdmin: Tab zur Verwaltung der Tabellenstruktur](https://www.ionos.at/digitalguide/fileadmin/_processed_/a/4/csm_phpmyadmin-tab-zur-verwaltung-der-tabellenstruktur_befa4b54d7.webp "phpMyAdmin: Tab zur Verwaltung der Tabellenstruktur")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-tab-zur-verwaltung-der-tabellenstruktur.png) Ausgewählte Spalten lassen sich auf Wunsch bearbeiten oder löschen       Das **Bearbeiten einer Spalte** erfolgt über die Schaltfläche „Change“. Sie gelangen in eine Bearbeitungsansicht, die der gleicht, die Sie bereits von der Tabellenerstellung kennen:

[![Bild: phpMyAdmin: Schaltfläche zur Bearbeitung von Spalten](https://www.ionos.at/digitalguide/fileadmin/_processed_/8/b/csm_phpmyadmin-schaltflaeche-zur-bearbeitung-von-spalten_1ed9a020b4.webp "phpMyAdmin: Schaltfläche zur Bearbeitung von Spalten")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-schaltflaeche-zur-bearbeitung-von-spalten.png) Ausgewählte Spalten werden in einer neuen Ansicht bearbeitet       Änderungen an der Tabellenstruktur können unter Umständen zu Datenverlust führen. Bevor sie bereits angelegte Tabellenspalten bearbeiten oder löschen, sollten Sie in jedem Fall ein **Back-up Ihrer Datenbank** anlegen. Wechseln Sie dazu in den Tab „Export“ (Exportieren), wählen Sie das gewünschte Datenformat für das Back-up aus und bestätigen Sie mit „Go“. Es öffnet sich ein Dialogfenster, in dem Ihr Webbrowser das Speicherziel für den Download abfragt. Eine Alternative zum Datenbank-Back-up via phpMyAdmin stellt das kostenlose Sicherungsprogramm [**MySQLDumper**](https://www.ionos.at/digitalguide/hosting/hosting-technik/backups-mit-mysqldumper-erstellen/ "Backups mit MySQLDumper erstellen") dar.

#### Tabelleneinträge erstellen

Um Ihre Tabelle via phpMyAdmin mit Daten zu befüllen, stehen Ihnen zwei Möglichkeiten offen: Entweder Sie importieren Datensätze aus einer externen Datei (z. B. aus einem Back-up) oder Sie erstellen Tabelleneinträge manuell. Wählen Sie dazu die Beispieltabelle *users* aus und navigieren Sie in den Tab „Insert“ (Einfügen).

phpMyAdmin zeigt Ihnen folgende **Dateneingabemaske**:

[![Bild: phpMyAdmin: Dateneingabemaske zum Erstellen von Tabelleneinträgen](https://www.ionos.at/digitalguide/fileadmin/_processed_/7/2/csm_phpmyadmin-dateneingabemaske-zum-erstellen-von-tabelleneintraegen_35bd47a320.webp "phpMyAdmin: Dateneingabemaske zum Erstellen von Tabelleneinträgen")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-dateneingabemaske-zum-erstellen-von-tabelleneintraegen.png) Tabelleneinträge erstellen Sie via phpMyAdmin im Tab „Insert“       Unter „Column“ sind die Spalten aufgelistet, die wir für die Tabelle *users* definiert haben. Unter „Type“ finden Sie die Information, welcher Datentyp für die jeweilige Spalte erwartet wird, sowie eine etwaige Zeichenbegrenzung in Klammern. Den Bereich „Function“ klammern wir aus und gehen direkt zu „Value“ über. In diesem Bereich definieren wir die Werte für die einzelnen Spalten unserer Beispieltabelle.

Im vorherigen Kapitel haben wir die Tabelle users so konfiguriert, dass die Datenfelder für die Spalten id, created\_at und updated\_at vom Datenbank-Management-System automatisch mit Inhalten versehen werden. In der Spalte id vergeben wir via AUTO\_INCREMENT eine laufende Identifizierungsnummer für jeden neuen Eintrag. Das Feld created\_at wird automatisch mit dem aktuellen Zeitstempel versehen. Und für updated\_at vergibt das System zunächst den Standardwert NULL. Wir müssen somit lediglich die Daten für die Spalten forename, surname, email und password manuell eintragen. Wir veranschaulichen dies an fiktiven Nutzerdaten:

- forename: *John*

- surname: *Doe*

- email: *john@doe.com*

- password: *qwertz*

[![Bild: phpMyAdmin: Beispieldaten in der Eingabemaske](https://www.ionos.at/digitalguide/fileadmin/_processed_/9/1/csm_phpmyadmin-beispieldaten-in-der-eingabemaske-1_09811f3a8f.webp "phpMyAdmin: Beispieldaten in der Eingabemaske")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-beispieldaten-in-der-eingabemaske-1.png)       [![Bild: phpMyAdmin: Beispieldaten in der Eingabemaske](https://www.ionos.at/digitalguide/fileadmin/_processed_/e/7/csm_phpmyadmin-beispieldaten-in-der-eingabemaske-2_fa594b6bae.webp "phpMyAdmin: Beispieldaten in der Eingabemaske")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-beispieldaten-in-der-eingabemaske-2.png) Fügen Sie beliebige Daten in die Eingabemaske ein und bestätigen Sie mit „Go“     Mit einem Klick auf „Go“ übertragen Sie die Daten in Ihre Tabelle. phpMyAdmin springt automatisch in den Tab „SQL“ und zeigt Ihnen die ausgeführte **Datenbankoperation als Statement** gemäß der SQL-Syntax an:

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

[![Bild: Befehl INSERT als Code gemäß der SQL-Syntax](https://www.ionos.at/digitalguide/fileadmin/_processed_/4/b/csm_befehl-insert-als-code-gemaess-sql-syntax_7def23d5ba.webp "Befehl INSERT als Code gemäß der SQL-Syntax")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/befehl-insert-als-code-gemaess-sql-syntax.png) phpMyAdmin gibt Ihnen jede Datenbankoperation als SQL-Statement aus     Prinzipiell lassen sich alle Datenbankoperationen, die Sie via phpMyAdmin komfortabel über eine grafische Benutzeroberfläche durchführen, auch in der Datenbanksprache SQL notieren. Dies gehört im Rahmen der Webentwicklung zum Standardvorgehen.

Sogenannte **SQL-Queries** (Abfragen) finden sich im Quellcode aller dynamischen Webanwendungen und ermöglichen dem Webserver die Interaktion mit der Datenbank. Die Datenbanksprache SQL stützt sich dabei auf Befehle – beispielsweise, um Daten abzurufen und im Rahmen der Programmausführung zu nutzen. Die wichtigsten SQL-Befehle *SELECT*, *INSERT*, *DELETE* und *UPDATE* sowie die Syntax der grundlegenden Datenbankoperationen thematisieren wir in den folgenden Kapiteln unsres MySQL-Tutorials.

Zunächst füllen wir unsere Tabelle *users* jedoch mit weiteren Nutzerdaten und schauen uns die Tabellenübersicht im Tab „Browse“ an:

[![Bild: phpMyAdmin: Tabellenübersicht im Tab „Browse“](https://www.ionos.at/digitalguide/fileadmin/_processed_/c/8/csm_phpmyadmin-tabellenuebersicht-im-tab-browse_d8e7524675.webp "phpMyAdmin: Tabellenübersicht im Tab „Browse“")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-tabellenuebersicht-im-tab-browse.png) In der Tabellenübersicht im Tab „Browse“ werden alle Tabelleneinträge nach dem Primärschlüssel sortiert aufgeführt     Ein Klick auf den jeweiligen **Spaltennamen sortiert** die Tabelle in der gewünschten Reihenfolge.

### Datenbankverbindung aufbauen

Nachdem wir unsere Bespieltabelle *users* mit Einträgen befüllt haben, werden wir uns in den nächsten Kapiteln anschauen, wie sich die hinterlegten **Daten via PHP über den Apache Webserver abfragen** lassen.

Dazu stellen wir im ersten Schritt eine **Datenbankverbindung** her. In PHP stehen Ihnen dazu drei Schnittstellen zur Verfügung: MySQL Extension, MySQL Improved Extension ([MySQLi](http://php.net/manual/de/book.mysqli.php "Mehr zu MySQLi auf php.net")) und PHP Data Objects ([PDO](http://php.net/manual/de/book.pdo.php "Mehr zu PDO auf php.net")).

- **MySQL-Extension:** Bei MySQL-Extension handelt es sich um eine überholte MySQL-Schnittstelle, 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 Statements](https://www.ionos.at/digitalguide/websites/web-entwicklung/prepared-statements-in-phpmysql/) noch benannte Parameter unterstützt.

- **MySQLi:** Bei [MySQLi](https://www.ionos.at/digitalguide/hosting/hosting-technik/mysqli-vorgestellt/ "MySQLi vorgestellt") handelt es sich um eine verbesserte Version der klassischen PHP-Erweiterung für den Zugriff auf MySQL-Datenbanken. Die Schnittstelle arbeitet sowohl prozedural als auch objektorientiert. Der Einsatz ist auf MySQL- und MariaDB-Datenbanken beschränkt.

- **PDO:** Bei PHP Data Objects (PDO) handelt es sich um eine objektorientierte Schnittstelle, die eine Abstraktionsschicht für den Datenzugriff zur Verfügung stellt. Somit lassen sich via PDO nicht nur MySQL-Datenbanken, sondern auch andere Datenbanksysteme wie [PostgreSQL](https://www.ionos.at/digitalguide/server/knowhow/postgresql/), Oracle, MSSQL oder SQLite in PHP einbinden.

Im Folgenden werden wir uns auf **Datenbankverbindungen via PDO** beschränken.

Um im Rahmen eines PHP-Scripts Anfragen an eine Datenbank stellen zu können, muss diese zunächst authentifiziert werden. Eine Datenbankverbindung via PDO wird mithilfe folgender Codezeile hergestellt:

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

Es empfiehlt sich, diese am Anfang eines jeden Scripts einzufügen, das Datenbankoperationen enthält.

Wir nutzen das PHP-Schlüsselwort *new*, um eine Instanz der PDO-Basisklasse zu erstellen. Deren Konstruktor erwartet grundsätzlich drei Parameter. Den **Data Source Name (DSN)** sowie einen Benutzernamen und das Passwort für die Datenbank – sofern vorhanden. Der DSN besteht in unserem Fall aus folgenden Parametern:

- **PDO-Datenbanktreiber:** *mysql*
- **Datenbankserver (*host=*):** *localhost*
- **Datenbankname (*dbname=*):** *test*
- **Zeichensatz (*charset=*):** *utf8*

Haben Sie für Ihre Datenbank keine Zugangsdaten definiert, nutzen Sie den Benutzernamen *root* und ein leeres Passwort:

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

Die Datenbankverbindung wird in der Variable *$pdo* gespeichert. Diese ermöglicht Ihnen, im späteren Verlauf des Programmcodes Bezug auf die Datenbankverbindung zu nehmen.

Wurde eine Verbindung zur Datenbank aufgebaut, können Sie im nachfolgenden Script-Code beliebig viele Abfragen an die Datenbank senden. Beenden Sie das Script, bricht auch die Datenbankverbindung ab.

## Datenabfragen mit *SELECT*, *INSERT*, *UPDATE* und *DELETE*

Um Daten aus unserer Datenbank abzurufen, greifen wir auf die **Datenbanksprache SQL** zurück. Diese ist semantisch an die englische Sprache angelehnt und bewusst einfach gehalten. Die SQL-Syntax ist weitgehend selbsterklärend.

In SQL arbeitet man mit Statements, diese werden auch als Abfragen oder Anfragen bezeichnet.

Eine einfache ***SELECT*-Query** besteht beispielsweise aus folgenden Komponenten:

```none
SELECT spalte FROM tabelle;
```

Sie definieren zunächst den SQL-Befehl *SELECT* und im Anschluss die jeweiligen Spalten und Tabellen, auf die sich der Befehl beziehen soll. Ein Strichpunkt schließt das Statement ab.

Zudem haben Sie die Möglichkeit, das Statement um eine **optionale Bedingung** sowie eine **Sortier- oder Gruppierfunktion** zu erweitern:

```none
SELECT spalte FROM tabelle WHERE bedingung ORDER BY sortierreihenfolge;
```

Dabei gilt die Konvention, dass SQL-Befehle großgeschrieben werden, Datenbank-, Tabellen- und Feldnamen hingegen klein. Diese dient lediglich der Lesbarkeit. SQL ist prinzipiell **eine formatfreie Sprache** und macht somit keinen Unterschied zwischen Groß- und Kleinschreibung.

Nutzen Sie Tabellen- und Spaltennamen die bereits vordefinierten SQL-Schlüsselwörtern entsprechen (was nicht zu empfehlen ist), müssen diese in **Backticks (``)** gesetzt werden.

Wir veranschaulichen die Syntax einfacher SQL-Statements am Beispiel der Befehle *SELECT*, *INSERT*, *UPDATE* und *DELETE*.

### SELECT

Den **Befehl *SELECT*** nutzen Sie, um ausgewählte Datenreihen (*rows*) aus einer beliebigen Anzahl von Tabellen abzurufen. Möchten Sie beispielsweise Vor- und Nachnamen sowie die E-Mail-Adressen aller Benutzer in der zuvor erstellten Beispieltabelle im Webbrowser ausgeben, erstellen Sie im Verzeichnis *htdocs* Ihrer XAMPP-Umgebung eine neue PHP-Datei *text.php* und fügen folgendes Skript ein:

```none
<?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 *&lt;?php*. In Zeile 2 stellen wir die Verbindung 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-Management-System angewiesen, 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öglichkeit, über ein beliebiges Array zu iterieren, sprich: eine Datenstruktur schrittweise durchzugehen. Welches Array wir iterieren möchten und wie die abgefragten Daten gespeichert werden sollen, definieren wir in Klammern hinter dem foreach-Konstrukt:

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

Die Variable *$pdo* spricht die gewünschte Datenbank über die in Zeile 2 definierte Verbindung an. Dieser senden wir mit der Funktion *query()* das in der Variable *$sql* gespeicherte 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 Tabellenzeile** durch. Wohin die ausgelesenen Daten gespeichert werden sollen, definiert das PHP-Schlüsselwort *as* in der Array-Variable *$row*.

Im ersten Durchgang der *foreach*-Schleife sähe dieses Array folgendermaßen aus.

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

Teil der *foreach*-Schleife ist im aktuellen Beispiel zudem die Textausgabe via *echo* in jedem Schleifendurchgang. Wir gehen somit jede Zeile der Tabelle *users* einzeln durch, lesen die hinterlegten Daten für die im SQL-Statement definierten Spalten ein und geben diese über den Webbrowser aus.

[![Bild: PHP-Skript mit SQL-Statement SELECT in Notepad++ und Browserausgabe via localhost](https://www.ionos.at/digitalguide/fileadmin/_processed_/9/1/csm_php-skript-mit-sql-statement-select_c6ceadc59e.webp "PHP-Skript mit SQL-Statement SELECT in Notepad++ und Browserausgabe via localhost")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/php-skript-mit-sql-statement-select.png) Das Browserfenster zeigt das Ergebnis der Skriptausführung       Sollen alle Spalten einer Datentabelle ausgelesen werden, verwenden Sie im SQL-Statement den Asterisken (*\**) als **Platzhalter**.

```none
<?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öglichkeit, alle in *users* hinterlegten Daten im Rahmen des Scripts zu verwenden. In folgendem Screenshot wurde die Textausgabe um den **Zeitstempel** der Eintragserstellung ergänzt:

[![Bild: PHP-Skript mit SQL-Statement inklusive Platzhalter und Browserausgabe via localhost](https://www.ionos.at/digitalguide/fileadmin/_processed_/a/d/csm_php-skript-mit-sql-statement-inklusive-platzhalter_c508093dda.webp "PHP-Skript mit SQL-Statement inklusive Platzhalter und Browserausgabe via localhost")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/php-skript-mit-sql-statement-inklusive-platzhalter.png) Textausgabe im Browser inklusive Zeitstempel       In beiden vorhergehenden Beispielen gibt uns der Webserver Nutzerdaten in der Reihenfolge aus, in der wir sie in die Tabelle *users* eingetragen haben (der ID entsprechend). Möchten Sie die Daten sortiert ausgeben, definieren Sie die Reihenfolge mithilfe des SQL-Schlüsselworts *ORDER BY*. In folgendem Beispiel werden die Daten gemäß der alphabetischen Reihenfolge der Vornamen ausgegeben:

```none
<?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 />";
}
?>
```

[![Bild: Sortierte Datenausgabe via ORDER BY](https://www.ionos.at/digitalguide/fileadmin/_processed_/a/a/csm_sortierte-datenausgabe-via-order-by_9a8b831b1d.webp "Sortierte Datenausgabe via ORDER BY")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/sortierte-datenausgabe-via-order-by.png) Via ORDER BY lassen sich Daten in gewünschter Reihenfolge ausgeben     ### INSERT

Die Erstellung von Datenbankeinträgen erfolgt nur selten manuell via phpMyAdmin. In der Regel werden Daten im Rahmen der Skriptausführung vom Webserver in die Datenbank geschrieben – z. B., wenn ein Internetnutzer ein Onlineformular auf einer Website ausfüllt oder einen Kundenkommentar in einem Webshop hinterlässt. In beiden Fällen kommt im Hintergrund der **SQL-Befehl *INSERT*** zum Einsatz. Ein SQL-Statement mit dem Befehl *INSERT* wird nach folgendem Schema erstellt:

```none
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 Bespieltabelle users einen weiteren Datensatz hinzuzufügen, könnte folgendermaßen aussehen:

```none
<?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 Verbindung zur Datenbank auf und speichern diese in der Variable *$pdo*. Anschließend definieren wird das SQL-Statement und speichern dieses in der Variable *$sql*. In Zeile 5 nutzen wir den Pfeil-Operator (*-&gt;*), um auf die Variable *$pdo* zuzugreifen und mithilfe der Funktion *exec()* das in *$sql* gespeicherte SQL-Statement auszuführen.

Um sicherzugehen, dass unser Skript lediglich einen Datensatz in die Tabelle *users* eingefügt, überprüfen wir die Anzahl der betroffenen Zeilen mithilfe einer ***if*-Bedingung**. Diese sorgt dafür, dass der String *New record created successfully* nur dann über den Webbrowser ausgegeben wird, wenn die Anzahl der eingefügten Datensätze 1 beträgt. Wird das Skript erneut ausgeführt, bleibt die Meldung aus. Doppelte Einträge verhindern wir dadurch, dass der Wert *email* als *UNIQUE* definiert wurde.

[![Bild: Der SQL-Befehl INSERT](https://www.ionos.at/digitalguide/fileadmin/_processed_/6/c/csm_sql-befehl-insert_7f7f3fb12e.webp "Der SQL-Befehl INSERT")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/sql-befehl-insert.png) Neue Datensätze fügen Sie Ihrer Datentabelle mithilfe des SQL-Befehls INSERT hinzu       Rufen wir die Übersicht unserer Beispieltabelle *users* in der Datenbank *test* auf, sehen wir, dass die Tabelle um den Datensatz 5 erweitert wurde. Die laufende Identifizierungsnummer und der Zeitstempel wurden wie beabsichtigt automatisch ergänzt.

[![Bild: phpMyAdmin: Aktualisierte Tabellenübersicht nach Datenbankoperation INSERT](https://www.ionos.at/digitalguide/fileadmin/_processed_/3/9/csm_phpmyadmin-aktualisierte-tabellenuebersicht-nach-datenbankoperation-insert_3bb08b9e49.webp "phpMyAdmin: Aktualisierte Tabellenübersicht nach Datenbankoperation INSERT")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-aktualisierte-tabellenuebersicht-nach-datenbankoperation-insert.png) Eine aktualisierte Ansicht Ihrer Datentabelle rufen Sie bei Bedarf via phpMyAdmin auf (neuer Datensatz rot hinterlegt)       ### UPDATE

Möchten Sie einen bestehenden Datensatz aktualisieren, nutzen Sie den **SQL-Befehl *UPDATE*** gemäß folgendem Grundschema:

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

In Worten ausgedrückt bedeutet dieses SQL-Statement: Wähle die angegebenen 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, überschreibt MySQL die betreffenden Felder in allen Datensätzen.

Wir haben es somit mit einem SQL-Statement zu tun, das eine Datenbankoperation an eine **Bedingung** knüpft. Übertragen auf unsere Beispieltabelle, ließe sich die E-Mail-Adresse des Nutzers John Doe durch folgendes PHP-Skript aktualisieren:

```none
<?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 definieren 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 aktualisieren somit lediglich den Datensatz mit dem Primärschlüssel 1. Darüber hinaus aktualisieren wir im selben SQL-Statement den Wert für die Spalte *updated\_at* mithilfe der MySQL-Funktion *now(),* die den aktuellen Zeitstempel zurückgibt. Ausgeführt wird das SQL-Statement wie zuvor mit der Codezeile *$pdo-&gt;exec($sql)* im Rahmen einer if-Bedingung.

[![Bild: Der SQL-Befehl UPDATE](https://www.ionos.at/digitalguide/fileadmin/_processed_/7/1/csm_sql-befehl-update_a59ec8b2ff.webp "Der SQL-Befehl UPDATE")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/sql-befehl-update.png) SQL-Statement mit UPDATE-Befehl und der TIMESTAMP-Funktion NOW()       War das Update erfolgreich, sollte Ihnen phpMyAdmin im Tab „Browse“ die aktualisierte Tabelle anzeigen:

[![Bild: phpMyAdmin: aktualisierte Tabellenübersicht nach Datenbankoperation UPDATE](https://www.ionos.at/digitalguide/fileadmin/_processed_/9/d/csm_phpmyadmin-aktualisierte-tabellenuebersicht-nach-datenbankoperation-update_0406504b1e.webp "phpMyAdmin: aktualisierte Tabellenübersicht nach Datenbankoperation UPDATE")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-aktualisierte-tabellenuebersicht-nach-datenbankoperation-update.png) Die Funktion NOW() überschreibt das Datenfeld updated\_at mit dem aktuellen Zeitstempel (aktualisierte Daten rot hinterlegt)       Im Beispiel haben wir eine E-Mail-Adresse aktualisiert und den Standardwert *NULL* in der Spalte *updated\_at* durch einen Zeitstempel ersetzt. Der *UPDATE*-Befehl ermöglicht zudem, **Werte aus einer Spalte in eine andere zu übertragen**. Einsetzen lässt sich diese Operation beispielsweise, wenn wir unsere Beispieltabelle *users* um die Spalte *email\_registration* erweitern. Dies gibt uns die Möglichkeit, zwischen zwei E-Mail-Adressen zu unterscheiden: eine, die bei der Registrierung verwendet wurde, und eine aktuelle Kontaktadresse, die sich mit der Zeit ändern kann. Zunächst sind beide Adressen jedoch gleich, sodass wir die Werte von einem Feld ins andere übertragen können. Dazu legen wir mit phpMyAdmin via „Add columns“ im Tab „Structure“ zunächst die neue Spalte *email\_registration* an:

[![Bild: phpMyAdmin: Tab „Structure“](https://www.ionos.at/digitalguide/fileadmin/_processed_/2/2/csm_phpmyadmin-tab-structure_f5180ec8f1.webp "phpMyAdmin: Tab „Structure“")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-tab-structure.png) Im Tab „Structure“ fügen Sie neue Tabellenspalten via „Add columns“ an gewünschter Position ein       Um die Werte zu übertragen, nutzen wir folgendes *UPDATE*-Statement:

```none
UPDATE users SET email_registration = email
```

Da wir alle Datensätze aktualisieren möchten, formulieren wir keine Bedingung für das Update.

```none
<?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 ausgeführt, überträgt das Datenbank-Management-System die Werte der Spalte *email* für alle Datensätze in die Spalte *email\_registration*.

[![Bild: Der SQL-Befehl UPDATE zum Übertragen von Spalteninhalten](https://www.ionos.at/digitalguide/fileadmin/_processed_/2/d/csm_sql-befehl-update-zum-uebertragen-von-spalteninhalten_a1a2f243ba.webp "Der SQL-Befehl UPDATE zum Übertragen von Spalteninhalten")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/sql-befehl-update-zum-uebertragen-von-spalteninhalten.png) Das Skript überträgt alle Werte der Spalte email in die Spalte email-registration und gibt die Anzahl der betroffenen Spalten im Browser aus.        [![Bild: phpMyAdmin: aktualisierte Tabellenübersicht nach UPADTE](https://www.ionos.at/digitalguide/fileadmin/_processed_/1/a/csm_phpmyadmin-aktualisierte-tabellenuebersicht-nach-update_13d1691016.webp "phpMyAdmin: aktualisierte Tabellenübersicht nach UPADTE")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-aktualisierte-tabellenuebersicht-nach-update.png) Die E-Mail-Adressen der ausgewählten Datensätze wurden von der Spalte email in email\_registration kopiert     ### DELETE

Löschen lassen sich Datenbankeinträge über den **SQL-Befehl *DELETE***. Diesen nutzen Sie nach folgendem Schema:

```none
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 identifizieren. Möchten Sie beispielsweise den Eintrag 5 in unserer Beispieltabelle löschen, gehen Sie wie folgt vor:

```none
<?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 Datenbankzeile. Möchten Sie lediglich Werte in bestimmten Spalten eines Datensatzes löschen, realisieren Sie dies mit einem *UPDATE*-Statement. Mit *UPDATE tabelle SET spalte = NULL WHERE …* können Sie einer Spalte den Wert *NULL* zuweisen – vorausgesetzt natürlich, dass Sie für die betreffende Spalte den *NULL*-Wert zugelassen haben.

## Prepared Statements

Mit PDO lassen sich Datenbankoperationen als sogenannte **Prepared Statements** umsetzen. Diese „vorgefertigten Abfragen“ sind heute gängige Praxis in der Webentwicklung und werden von allen modernen Datenbank-Management-Systemen unterstützt. In den bisherigen Beispielen haben wir Parameterwerte direkt im SQL-Statement übergeben. Prepared Statements hingegen arbeiten mit Platzhaltern, die erst nachträglich mit Werten besetzt werden. Dies ermöglicht es dem Datenbank-Management-System, die **Gültigkeit von Parametern** zu prüfen, bevor diese verarbeitet werden. Prepared Statements bieten, sofern diese im Quelltext konstant umgesetzt werden, einen effektiven Schutz gegen **[SQL-Injection](https://www.ionos.at/digitalguide/server/sicherheit/sql-injection-grundlagen-und-schutzmassnahmen/)**. Bei diesem Angriffsmuster erstellen oder verändern Hacker SQL-Befehle, um an sensible Daten zu gelangen, Daten zu überschreiben oder eigene Befehle in ein System einzubringen. SQL-Injection basiert auf einer bekannten **Sicherheitslücke im Umgang mit SQL-Datenbanken**: Werden Benutzereingaben beispielsweise via *$\_GET* mit statischen Parametern übergeben, bietet dies Angreifern die Möglichkeit, den Input mit Metazeichen anzureichern, die zu ungewollten Effekten führen können, sofern diese ohne Maskierung in den SQL-Interpreter gelangen. Dem lässt sich durch parametrisierte Abfragen effektiv vorbeugen. Prepared Statements fungieren dabei als Templates für SQL-Befehle, die getrennt von den eigentlichen Parametern an die Datenbank übermittelt werden. Diese validiert die übergebenen Daten, maskiert Metazeichen automatisch und fügt die Parameter anstelle der Platzhalter in das SQL-Statement ein. Prepared Statements bieten neben dem Sicherheitsaspekt zudem einen **Performance-Vorteil**. Dieser zeigt sich, wenn derselbe SQL-Befehl in einer Schleife mit verschiedenen Parametern ausgeführt werden soll. Nachdem ein Prepared Statement einmal geparst wurde, liegt dieses im Datenbanksystem vor und muss dort lediglich mit neuen Parametern ausgeführt werden. Komplizierte Abfragen lassen sich so deutlich beschleunigen. In PDO werden Prepared Statements mithilfe der Funktion prepare() umgesetzt. Diese bereitet ein Statement für die Ausführung vor und gibt ein Statement-Objekt zurück. Als Platzhalter für die jeweiligen Werte kommt entweder das Fragezeichen (?) oder ein benannter Parameter (Named Parameter) zum Einsatz.

### Prepared Statements mit unbenannten Parametern

Folgendes Codebeispiel zeigt die Datenbankoperation INSERT als Prepared Statement mit **unbenannten Parametern**:

```none
<?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 gewünschten Abfrage und speichern dieses im Array *$statement*. Statt konkreter Parameterwerte kommt das Fragezeichen als Platzhalter zum Einsatz.

Beinhaltet ein SQL-Statement lediglich Platzhalter, müssen separat übergebene Werte im nachfolgenden Code an dieses gebunden werden. In PHP kommt dazu die Funktion *bindParam()* zum Einsatz. Wir nutzen den Pfeil-Operator (*-&gt;*), um auf die *bindParam()*-Methode des Objekts *$statement* zuzugreifen und diesem Variablen zuzuweisen (1 entspricht dem ersten Fragezeichen, 2 dem zweiten usw.).

Das so erstellte SQL-Template kann nun beliebig oft mit den gewünschten Parametern ausgeführt werden. Im aktuellen Beispiel definieren wir Variablenwerte für zwei Datensätze. Die Ausführung des vorgefertigten SQL-Statements erfolgt für jeden Datensatz via *execute()*.

### Prepared Statements mit benannten Parametern

Übersichtlicher als der Fragezeichenplatzhalter sind **benannte Parameter**. Dabei handelt es sich um benutzerdefinierte Platzhalter, die Sie nach Bedarf nach folgendem Schema benennen können:

```none
:beispiel
```

Benannte Parameter dürfen weder Leerzeichen noch Bindestriche (*-*) enthalten. Verwenden Sie stattdessen den Unterstrich (**\_**).

Folgendes Beispiel zeigt die Datenbankoperation INSERT als Prepared Statement mit benannten Parametern:

```none
<?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 Spaltennamen unserer Beispieltabelle entsprechend benannt. Durch die Syntax vorgegeben wird dies nicht. Eine **einheitliche Benennung** ist im Sinne eines gut lesbaren Quellcodes jedoch zu empfehlen. Die Zuweisung der Variablenwerte sowie die Ausführung des SQL-Statements erfolgt analog zum vorhergehenden Beispiel.

## Datums- und Zeitfunktionen in MySQL

MySQL und MariaDB unterstützen diverse Funktionen, um mit Datums- und Zeitangaben zu arbeiten. Eine vollständige Liste finden Sie [hier](http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate "Datum und Zeitfunktionen in der offiziellen MySQL-Dokumentation"). In unserem MySQL-Tutorial für Einsteiger beschränken wir uns auf eine Auswahl.

<table>
  <thead>
    <tr>
      <th>Datums- und Zeitfunktion</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CURRENT\_TIMESTAMP() / NOW()</td>
      <td>Im Bespiel zum SQL-Befehl UPDATE haben Sie bereits die Funktion NOW() kennengelernt. Bei dieser handelt es sich lediglich um ein Synonym der Funktion CURRENT\_TIMESTAMP(). Zur Anwendung kommt die Funktion immer dann, wenn im Rahmen einer Datenbankoperation das aktuelle Datum inklusive Uhrzeit festgehalten werden soll.</td>
    </tr>
    <tr>
      <td>CURDATE() / CURRENT\_DATE()</td>
      <td>Die Funktion CURDATE() gibt das aktuelle Datum zurück.</td>
    </tr>
    <tr>
      <td>CURTIME() / CURRENT\_TIME()</td>
      <td>Die Funktion CURTIME() gibt die aktuelle Uhrzeit zurück.</td>
    </tr>
    <tr>
      <td>DAY() / DAYOFMONTH()</td>
      <td>Gibt den Tag des Monats zurück (0–31); benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
    <tr>
      <td>DAYOFWEEK()</td>
      <td>Gibt den Tag der Woche zurück (1 = Sonntag); benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
    <tr>
      <td>MONTH()</td>
      <td>Gibt den Monat zurück (1–12); benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
    <tr>
      <td>YEAR()</td>
      <td>Gibt eine Jahreszahl zurück (1000–9999, 0); benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
    <tr>
      <td>DATE()</td>
      <td>Extrahiert das Datum aus einer Zeit- oder Datumsangabe; benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
    <tr>
      <td>TIME()</td>
      <td>Extrahiert die Uhrzeit aus seiner Zeit oder Datumsangabe; benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
    <tr>
      <td>DATE\_FORMAT()</td>
      <td>Formatiert eine Zeit oder Datumsangabe gemäß der angegeben Parameter; benötigt ein Datum oder Timestamp als Argument</td>
    </tr>
  </tbody>
</table>

Ein mögliches Anwendungsszenario für Zeit- und Datumsfunktionen in MySQL sind beispielsweise Datenbankabfragen, bei denen alle Datensätze ausgelesen werden sollen, die **an einem bestimmten Tag erstellt** wurden.

Folgendes Skript gibt uns alle Datensätze unserer Beispieltabelle *users* aus, die heute erstellt wurden:

```none
<?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 auszugeben, nutzen wir im SQL-Statement folgende Bedingung:

```none
DATE(created_at) = CURDATE()
```

Zunächst extrahieren wir mithilfe der Funktion *DATE()* das Datum aus dem in der Spalte *created\_at* gespeicherten Zeitstempel und gleichen diesen im nächsten Schritt mit dem aktuellen Datum ab. Der Befehl *SELECT* wählt somit lediglich die Einträge aus, deren Zeitstempel mit dem heutigen Datum übereinstimmen.

Alternativ könnten wir den Eintrag auswählen, den wir am 16.12.2016 aktualisiert haben. Dazu müssen wir lediglich die Bedingung unseres SQL-Statements anpassen:

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

In diesem Fall wird die aus dem Zeitstempel extrahierte Datumsangabe mit einem konkreten Datum abgeglichen. Darüber hinaus können Sie Abfragen auf ein bestimmtes Jahr, einen Monat oder eine Tag eingrenzen.

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

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

Neben dem Gleichheitszeichen unterstützt SQL in Bedingungen zudem folgende Vergleichsoperatoren:

<table>
  <thead>
    <tr>
      <th>Vergleichsoperator</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>=</td>
      <td>ist gleich</td>
    </tr>
    <tr>
      <td>&lt;</td>
      <td>kleiner als</td>
    </tr>
    <tr>
      <td>&gt;</td>
      <td>größer als</td>
    </tr>
    <tr>
      <td>&lt;=</td>
      <td>kleiner oder gleich</td>
    </tr>
    <tr>
      <td>&gt;=</td>
      <td>größer oder gleich</td>
    </tr>
    <tr>
      <td>!=</td>
      <td>ungleich</td>
    </tr>
  </tbody>
</table>

Darüber hinaus lassen sich mehrere Bedingungen mit logischen Operatoren verknüpfen:

<table>
  <thead>
    <tr>
      <th>Logischer Operator</th>
      <th>Beschreibung</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>OR bzw.</td>
      <td>Logisches Oder</td>
    </tr>
    <tr>
      <td>AND bzw. &amp;&amp;</td>
      <td>Logisches Und</td>
    </tr>
  </tbody>
</table>

Folgendes Statement wählt beispielsweise alle Einträge aus, die nach Februar und vor April erstellt wurden:

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

Bisher haben wir Datums- und Zeitangaben in unserer Datenbank im vorgegeben Format gespeichert. Mit MySQL und MariaDB sind Sie auf dieses jedoch nicht festgelegt. Die Funktion *DATE\_FORMAT()* bietet Ihnen die Möglichkeit, **Daten und Uhrzeiten mit optionalen Parametern beliebig zu formatieren**.

```none
<?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 nachstehende Tabelle zeigt mögliche Parameter für die Funktion *DATE\_FORMAT()* gemäß MySQL-Dokumentation.

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Beschreibung</th>
      <th>Wertebereich/Beispiele</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>%c</td>
      <td>Numerische Monatsangabe mit 1 oder 2 Stellen</td>
      <td>0 bis 12</td>
    </tr>
    <tr>
      <td>%d</td>
      <td>Numerische Angabe des Tags im Monat mit 2 Stellen</td>
      <td>00 bis 31</td>
    </tr>
    <tr>
      <td>%e</td>
      <td>Numerische Angabe des Tags im Monat mit 1 oder 2 Stellen</td>
      <td>0 bis 31</td>
    </tr>
    <tr>
      <td>%H</td>
      <td>Stundenanzeige mit 2 Stellen</td>
      <td>00 bis 23</td>
    </tr>
    <tr>
      <td>%i</td>
      <td>Numerische Minutenangabe mit 2 Stellen</td>
      <td>00 bis 59</td>
    </tr>
    <tr>
      <td>%j</td>
      <td>Numerische Angabe des Tags im Jahr mit 3 Stellen</td>
      <td>001 bis 366</td>
    </tr>
    <tr>
      <td>%k</td>
      <td>Numerische Stundenangabe mit 1 oder 2 Stellen</td>
      <td>0 bis 23</td>
    </tr>
    <tr>
      <td>%M</td>
      <td>Name des Monats im aktuellen Gebietsschema</td>
      <td>January, February, March etc.</td>
    </tr>
    <tr>
      <td>%m</td>
      <td>Numerische Monatsangabe mit 2 Stellen</td>
      <td>00 bis 12</td>
    </tr>
    <tr>
      <td>%s</td>
      <td>Numerische Sekundenanzeige mit 2 Stellen</td>
      <td>00 bis 59</td>
    </tr>
    <tr>
      <td>%T</td>
      <td>Zeit im 24-Stundenfomat (Kurzform für '%H:%i:%S'.</td>
      <td>HH:MM:SS</td>
    </tr>
    <tr>
      <td>%u</td>
      <td>Nummer der Woche im Jahr, beginnend mit Montag</td>
      <td>00 bis 53</td>
    </tr>
    <tr>
      <td>%W</td>
      <td>Name des Wochentags im aktuellen Gebietsschema</td>
      <td>Monday, Tuesday etc.</td>
    </tr>
    <tr>
      <td>%w</td>
      <td>Wochentag</td>
      <td>0 = Sunday, 6 = Saturday</td>
    </tr>
    <tr>
      <td>%Y</td>
      <td>Jahreszahl mit 4 Stellen</td>
      <td>z. B. 2016</td>
    </tr>
    <tr>
      <td>%y</td>
      <td>Jahreszahl mit 2 Stellen</td>
      <td>z. B. 16</td>
    </tr>
  </tbody>
</table>

## MySQL-Fehlermeldungen

Lässt sich ein Skript nicht wie gewünscht ausführen, liegt dies in der Regel an syntaktischen Fehlern im Quellcode oder falsch benannten Tabellen, Spalten und Variablen. Der Datenbankserver spielt in diesem Fall jedoch nicht zwangsläufig eine Fehlermeldung aus. Oft bleibt das gewünschte Ergebnis ohne Hinweis auf die fehlgeschlagene Operation aus.

Mit *errorInfo()* stellt PDO daher eine Funktion bereit, mit der sich erweiterte **Fehlerinformationen zur letzten Datenbankoperation** gezielt abrufen lassen – beispielsweise, um diese über den Webbrowser auszugeben.

In folgendem **Skript zur Aktualisierung der E-Mail-Adresse** kommt die Funktion *errorInfo()* in Kombination mit einer *if-*Schleife zum Einsatz. Deren Bedingung ist die korrekte Ausführung des SQL-Statements. Wird dieses fehlerfrei ausgeführt, gibt der Webserver den String *Update successful* aus. Andernfalls wird der unter *else* aufgeführte Code ausgeführt.

Im aktuellen Beispiel informieren wir den Nutzer darüber, dass ein SQL-Fehler aufgetreten ist und geben das betreffende SQL-Statement sowie erweiterte Fehlerinformationen via errorInfo() aus:

```none
<?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 Information ausgegeben:

```none
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 Beispieltabelle haben wir jedoch mit *users* benannt. Der SQL-Server kann die angeforderte Tabelle daher nicht finden und gibt die Meldung *Table 'test.user' doesn't exist* aus. Fehlerursache ist somit lediglich ein Tippfehler im SQL-Statement, der schnell behoben ist.

Bei den Rückgabewerten der Funktion errorInfo() handelt es sich um ein Array, das drei Elemente enthält:

\[0\] = SQL-Fehlercode

\[1\] = Treiberspezifischer Fehlercode

\[2\] = Treiberspezifische Fehlermeldung

Welche Informationen Sie via errorInfo() abrufen, definieren Sie durch die Angabe des gewünschten Elements in eckigen Klammern.

[![Bild: Ausgabe einer SQL-Fehlerinformation](https://www.ionos.at/digitalguide/fileadmin/_processed_/e/1/csm_ausgabe-einer-sql-fehlerinformation_63592d87fa.webp "Ausgabe einer SQL-Fehlerinformation")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/ausgabe-einer-sql-fehlerinformation.png) Ausgabe der Fehlerinformationen über den Webbrowser       In der Praxis werden detaillierte Fehlerinformationen jedoch nur selten über den Webbrowser ausgegeben. Endanwender können mit diesen Angaben in der Regel nur wenig anfangen. Potenzielle Angreifer hingegen nutzen Fehlermeldungen mitunter, um SQL-Abfragen nachzuvollziehen und so Schwachstellen einer Anwendung ausfindig zu machen. Es empfiehlt sich daher, Nutzer nur allgemein über einen Fehlerfall zu informieren und **konkrete Fehlerinformationen intern zu speichern**. Umsetzen lässt sich dies beispielsweise folgendermaßen:

```none
<?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ückgabewert der Funktion *errorInfo()* via *echo* im Webbrowser auszugeben, speichern wir diesen zusammen mit dem aktuellen Zeitstempel sowie dem Pfad zur Datei und dem betreffenden SQL-Statement in der Variable *$error*.

Die **PHP-Funktion** ***DATE()*** gibt den Unix-Zeitstempel im angegebenen Format aus. Die sogenannte magische Konstante *\_\_FILE\_\_* liefert uns den vollständigen Pfad zur Datei *test.php*. Das aktuelle SQL-Statement rufen wir wie im vorherigen Beispiel via *$statement-&gt;queryString* ab. Im Anschluss werden die in *$error* gespeicherten Daten mithilfe der Funktion *file\_put\_contents()* als Text in der Datei *sqlerrors.log* im Ordner *htdocs* gespeichert.

[![Bild: Gespeicherte Fehlerinformation in der Datei sqlerrors.log](https://www.ionos.at/digitalguide/fileadmin/_processed_/e/9/csm_gespeicherte-fehlerinformation-in-der-datei-sqlerrors-log_d73c829364.webp "Gespeicherte Fehlerinformation in der Datei sqlerrors.log")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/gespeicherte-fehlerinformation-in-der-datei-sqlerrors-log.png) Die gespeicherten Fehlerinformationen finden Sie in der Datei sqlerrors.log       ##  JOIN

Wie im Kapitel zu relationalen Datenbanken angesprochen, können wir Daten aus mehreren Tabellen gleichzeitig abfragen. Nachdem Sie nun mit den grundlegenden Datenbankoperationen vertraut sind, greifen wir dies erneut auf und demonstrieren Ihnen, wie Sie verschiedene Tabellen Ihrer Datenbank in einem Join (dt. „Verbindung“ oder „Verbund“) verknüpfen.

Die Zusammenführung von Tabellen im Rahmen einer Abfrage erfolgt mithilfe des SQL-Befehls *JOIN*. Dabei werden zwei oder mehr **normalisierte Tabellen** über gemeinsame Spalten verbunden. Realisieren lässt sich dies via Fremdschlüssel.

Wir demonstrieren die Verknüpfung von Datenbanktabellen an folgendem Beispiel:

[![Bild: Datenbank](https://www.ionos.at/digitalguide/fileadmin/_processed_/3/4/csm_datenbankabfrage-2_00a0f5f98a.webp "Datenbank")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/datenbankabfrage-2.png)      Die Tabelle zeigt eine Auswahl unserer Lieblingssongs aus den 1960er Jahren und soll als **Negativbeispiel für ein schlechtes Datenbankdesign** dienen.

Auf den ersten Blick fällt auf, dass die Tabelle zahlreiche redundante Datenfelder aufweist. Diese beseitigen wir, in dem wir die Daten im Rahmen der Normalisierung in separate Tabellen aufteilen und mithilfe von Fremdschlüsseln verknüpfen.

### Normalformen

Gutes Datenbankdesign zeichnet sich durch ein **Minimum an Redundanz** aus. Vermeiden lassen sich doppelte Einträge durch die Normalisierung von Datentabellen. Im Rahmen des relationalen Datenbankmodells haben sich **drei aufeinander aufbauende Normalformen** etabliert, die Ihnen feste Regeln für die optimale Datenstrukturierung vorgeben.

#### 1. Normalform

Eine Tabelle entspricht der 1. Normalform, wenn alle Attributwerte atomar sind. Als atomar gelten Attributwerte, wenn diese **lediglich eine Information** enthalten. Verdeutlichen lässt sich dies an unserem Negativbeispiel.

Betrachten Sie beispielsweise die Spalten *album\_title* und *interpret* in der Tabelle *album*. Statt jede in der Tabelle enthaltene Information in einer separaten Spalte aufzuführen, haben wir es uns vermeintlich leicht gemacht und Informationen zum Erscheinungsjahr eines Albums sowie zum Bestehen einer Band einfach in Klammern hinter dem Albumtitel bzw. der Interpretenangabe notiert. Doch diese Schludrigkeit rächt sich spätestens dann, wenn wir beispielsweise alle Titel abfragen wollen, die in einem bestimmten Jahr veröffentlicht wurden.

Wir empfehlen daher Datentabellen stets nach den Regeln der 1. Normalform zu erstellen. Für unser Tabellenbeispiel würde dies folgendermaßen aussehen:

<table>
  <tbody>
    <tr>
      <td colspan="7"><strong>album</strong></td>
    </tr>
    <tr>
      <td><strong>album\_id</strong></td>
      <td><strong>album\_title</strong></td>
      <td><strong>released</strong></td>
      <td><strong>interpret</strong></td>
      <td><strong>years\_active</strong></td>
      <td><strong>track</strong></td>
      <td><strong>title</strong></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Revolver</td>
      <td>1966</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
      <td>2</td>
      <td>Eleanor Rigby</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Revolver</td>
      <td>1966</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
      <td>8</td>
      <td>Good Day Sunshine</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Abbey Road</td>
      <td>1969</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
      <td>1</td>
      <td>Come Together</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Abbey Road</td>
      <td>1969</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
      <td>7</td>
      <td>Here Comes the Sun</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Beggars Banquet</td>
      <td>1968</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
      <td>1</td>
      <td>Sympathy for the Devil</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Beggars Banquet</td>
      <td>1968</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
      <td>6</td>
      <td>Street Fighting Man</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Sticky Fingers</td>
      <td>1971</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
      <td>1</td>
      <td>Brown Sugar</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Sticky Fingers</td>
      <td>1971</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
      <td>3</td>
      <td>Wild Horses</td>
    </tr>
  </tbody>
</table>

Sämtliche Daten sind nun separiert und somit ohne weiteres auslesbar. Unsere Tabelle enthält jedoch nach wie vor Redundanzen. Wie sich diese beseitigen lassen, zeigen wir in den nächsten Schritten.

#### 2. Normalform

Eine Tabelle entspricht der 2. Normalform, wenn alle Bedingungen der 1. Normalform erfüllt sind und jedes Nichtschlüsselattribut vom gesamten Primärschlüssel der Tabelle voll funktional abhängig ist.

Oft enthalten Datentabellen lediglich eine Spalte, die als Primärschlüssel fungiert. Tabellen dieser Art entsprechen automatisch der 2. Normalform, wenn die Bedingungen der 1. Normalform erfüllt sind. Mitunter kommt es aber vor, dass sich der Primärschlüssel einer Tabelle aus zwei Spalten ergibt. Dies ist bei unserem Tabellenbeispiel der Fall.

Um einen gewünschten Titel aus der *title*-Spalte der Tabelle *title* abzurufen, bräuchten wir zum einen die *album\_id* als auch die in der Spalte *track* hinterlegte Titelnummer. Der Titel *Sympathy for the Devil* ließe sich beispielweise durch den Primärschlüssel *album\_ID = 3 AND track = 1* abrufen. Wir haben es also mit einem **zusammengesetzten Primärschlüssel** zu tun. Dieser wird jedoch ausschließlich für Abfragen benötigt, die sich auf die *title-*Spalte beziehen. Die Spalten *album\_title, released, interpret* und *years\_active* sind ausschließlich von der *album\_id* abhängig. Es besteht für diese Spalten somit keine volle funktionale Abhängigkeit vom gesamten Primärschlüssel. Die Bedingungen der 2. Normalform sind nicht erfüllt.

Dies lässt sich ändern, indem wir die Spalte title in eine neue Tabelle auslagern und durch einen Fremdschlüssel (album\_id) mit der Ausgangstabelle verknüpfen:

<table>
  <tbody>
    <tr>
      <td colspan="5"><strong>album</strong></td>
    </tr>
    <tr>
      <td><strong>album\_id</strong></td>
      <td><strong>album\_title</strong></td>
      <td><strong>released</strong></td>
      <td><strong>interpret</strong></td>
      <td><strong>years\_active</strong></td>
    </tr>
    <tr>
      <td>1</td>
      <td>Revolver</td>
      <td>1966</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Abbey Road</td>
      <td>1969</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Beggars Banquet</td>
      <td>1968</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Sticky Fingers</td>
      <td>1971</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td colspan="3"><strong>title</strong></td>
    </tr>
    <tr>
      <td><strong>album\_id</strong></td>
      <td><strong>track</strong></td>
      <td><strong>title</strong></td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>Eleanor Rigby</td>
    </tr>
    <tr>
      <td>1</td>
      <td>8</td>
      <td>Good Day Sunshine</td>
    </tr>
    <tr>
      <td>2</td>
      <td>1</td>
      <td>Come Together</td>
    </tr>
    <tr>
      <td>2</td>
      <td>7</td>
      <td>Here Comes the Sun</td>
    </tr>
    <tr>
      <td>3</td>
      <td>1</td>
      <td>Sympathy for the Devil</td>
    </tr>
    <tr>
      <td>3</td>
      <td>6</td>
      <td>Street Fighting Man</td>
    </tr>
    <tr>
      <td>4</td>
      <td>1</td>
      <td>Brown Sugar</td>
    </tr>
    <tr>
      <td>4</td>
      <td>3</td>
      <td>Wild Horses</td>
    </tr>
  </tbody>
</table>

Die überarbeitete Tabelle *album* beinhaltet lediglich einen **einteiligen Primärschlüssel** und erfüllt somit automatisch die Bedingungen der 2. Normalform. Die neue Tabelle *title* beinhaltet lediglich die Nicht-Schlüsselspalte *title*. Diese ist von beiden Teilen des Primärschlüssels (*album\_id* und *track*) voll funktional abhängig und entspricht somit ebenfalls der 2. Normalform.

Doch auch in der 2. Normalform enthält unsere Datentabelle *album* redundante Einträge.

#### 3. Normalform

Soll eine Tabelle der 3. Normalform entsprechen, müssen alle Bedingungen der 2. Normalform (und somit auch der 1. Normalform) erfüllt sein. Zudem gilt, dass kein Nichtschlüsselattribut in transitiver Abhängigkeit zu einem Schlüsselattribut stehen darf. Diese Bedingung hört sich kompliziert an, lässt sich jedoch mit wenigen Worten erklären: Eine **transitive Abhängigkeit** besteht immer dann, wenn ein Nichtschlüsselattribut von einem anderen Nichtschlüsselattribut abhängt.

Genau dies trifft auf unsere Beispieltabelle *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 Jahresangabe des Bandbestehens wiederum vom Interpreten und somit transitiv von der *album\_id* abhängig. Dies hat den Nachteil, dass das Datenbank-Management-System jedes Mal, wenn ein neues Album eines bereist aufgeführten Interpreten eingetragen wird, automatisch einen redundanten Wert in der Spalte *years\_active* abspeichert.

Um die Bedingungen der 3. Normalform zu erfüllen und somit alle Redundanzen aus unserer Tabelle zu entfernen, müssen wir auch die Spalte *interpret* inklusive *years\_*active** in eine separate Tabelle auslagern und via Fremdschlüssel *(interpret\_id)* mit der Ausgangstabelle *album* verknüpfen.

Wir erhalten somit drei normalisierte Tabellen: interpret, album und title.

<table>
  <tbody>
    <tr>
      <td colspan="3"><strong>interpret</strong></td>
    </tr>
    <tr>
      <td><strong>interpret\_id</strong></td>
      <td><strong>interpret</strong></td>
      <td><strong>years\_active</strong></td>
    </tr>
    <tr>
      <td>1</td>
      <td>The Beatles</td>
      <td>1960–1970</td>
    </tr>
    <tr>
      <td>2</td>
      <td>The Rolling Stones</td>
      <td>1962–present</td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td colspan="4"><strong>album</strong></td>
    </tr>
    <tr>
      <td><strong>album\_id</strong></td>
      <td><strong>interpret\_id</strong></td>
      <td><strong>album\_title</strong></td>
      <td><strong>released</strong></td>
    </tr>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>Revolver</td>
      <td>1966</td>
    </tr>
    <tr>
      <td>2</td>
      <td>1</td>
      <td>Abbey Road</td>
      <td>1969</td>
    </tr>
    <tr>
      <td>3</td>
      <td>2</td>
      <td>Beggars Banquet</td>
      <td>1968</td>
    </tr>
    <tr>
      <td>4</td>
      <td>2</td>
      <td>Sticky Fingers</td>
      <td>1971</td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td colspan="3"><strong>title</strong></td>
    </tr>
    <tr>
      <td><strong>album\_id</strong></td>
      <td><strong>track</strong></td>
      <td><strong>title</strong></td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>Eleanor Rigby</td>
    </tr>
    <tr>
      <td>1</td>
      <td>8</td>
      <td>Good Day Sunshine</td>
    </tr>
    <tr>
      <td>2</td>
      <td>1</td>
      <td>Come Together</td>
    </tr>
    <tr>
      <td>2</td>
      <td>7</td>
      <td>Here Comes the Sun</td>
    </tr>
    <tr>
      <td>3</td>
      <td>1</td>
      <td>Sympathy for the Devil</td>
    </tr>
    <tr>
      <td>3</td>
      <td>6</td>
      <td>Street Fighting Man</td>
    </tr>
    <tr>
      <td>4</td>
      <td>1</td>
      <td>Brown Sugar</td>
    </tr>
    <tr>
      <td>4</td>
      <td>3</td>
      <td>Wild Horses</td>
    </tr>
  </tbody>
</table>

Möchten wir nun einen bestimmten Titel in unserer Datenbank inklusive der Informationen zum Album sowie zum Interpreten ausgeben, müssen wir die drei separaten **Tabellen mithilfe des SQL-Befehls *JOIN* und der jeweiligen Fremdschlüssel verbinden**.

### Fremdschlüssel via phpMyAdmin definieren

Sofern Sie als **Datenbank-Engine InnoDB** gewählt haben, definieren Sie Fremdschlüsselbeziehungen bequem über die grafische Benutzeroberfläche Ihrer Administrationssoftware phpMyAdmin. Dabei kann der Primärschlüssel einer Tabelle als Fremdschlüssel in beliebig vielen anderen Tabellen zum Einsatz kommen.

In unserem Beispiel benötigen wir zwei Verbindungen, um die drei normalisierten Tabellen *album*, *interpret* und *title* zu verknüpfen:

- Für die erste Verbindung nutzen wir den Primärschlüssel *album\_id* der Tabelle *album* als Fremdschlüssel in der Tabelle *title*.

- Für die zweite Verbindung nutzen wir den Primärschlüssel *interpret\_id* der Tabelle *interpret* als Fremdschlüssel in der Tabelle *album*.

Veranschaulichen lassen sich die **Fremdschlüsselbeziehungen** durch folgende Grafik:

[![Bild: Normalisierte Datentabellen mit Fremdschlüssel-Verknüpfung](https://www.ionos.at/digitalguide/fileadmin/_processed_/d/a/csm_normalisierte-datentabellen-mit-fremdschluessel-verknuepfung_ab94586699.webp "Normalisierte Datentabellen mit Fremdschlüssel-Verknüpfung")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/normalisierte-datentabellen-mit-fremdschluessel-verknuepfung.png) Die normalisierten Tabellen album, title und interpret, verknüpft durch Fremdschlüssel       Bei der Verknüpfung von Datentabellen gilt: Eine Spalte, die als Fremdschlüssel fungieren soll, muss mit den Attributen *UNIQUE* oder *INDEX* versehen werden.

Die Beziehung zwischen Primärschlüssel und Fremdschlüssel entspricht in der Regel dem Beziehungstyp 1:n. Jedes Datenfeld in der Primärschlüssel-Spalte der Tabelle A steht mit beliebig vielen (n) Datenfeldern in der Fremdschlüssel-Spalte der Tabelle B in Beziehung. Doch jedes Datenfeld in der Fremdschlüssel-Spalte der Tabelle B bezieht sich immer auf genau ein Datenfeld in der Primärschlüssel-Spalte der Tabelle A. Beispielsweise haben wir in der Primärschlüssel-Spalte album\_id in der Tabelle album vier Einträge, die über den Fremdschlüssel title.album\_id mit acht Einträgen der Tabelle title verknüpft sind.

[![Bild: 1:n-Beziehung zwischen den Primärschlüssel und Fremdschlüssel](https://www.ionos.at/digitalguide/fileadmin/_processed_/2/a/csm_beziehung-zwischen-den-primaerschluessel-und-fremdschluessel_28fe20fdad.webp "1:n-Beziehung zwischen den Primärschlüssel und Fremdschlüssel")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/beziehung-zwischen-den-primaerschluessel-und-fremdschluessel.png) Primärschlüssel und Fremdschlüssel stehen in einer 1:n-Beziehung       Um die gewünschten **Verknüpfungen einzurichten**, legen wir die Tabellen *album*, *interpret* und *title* in phpMyAdmin an und definieren unsere Primärschlüssel im Rahmen der Tabellenerstellung wie bereits beschrieben über die Option „Index“. Achten sie zudem darauf, dass Spalten, die später als Fremdschlüssel fungieren sollen, ebenfalls über die Index-Option als *INDEX* oder *UNIQUE* markiert werden. Für 1:n-Beziehungen eignet sich aber nur *INDEX*, denn die Werte in einem *UNIQUE*-Feld dürfen sich schließlich nicht wiederholen.

Im nächsten Schritt definieren wir die **Fremdschlüssel**. Wir demonstrieren dies am Beispiel der Tabelle *album*. Dazu wählen wir die Tabelle im Navigation-Panel aus und rufen den Tab *Structure* in der Menüleiste auf. Hier findet sich die Schaltfläche „Relation view“ (Beziehungsansicht):

[![Bild: phpMyAdmin: Tab „Structure“, Schaltfläche „Relation view“](https://www.ionos.at/digitalguide/fileadmin/_processed_/a/f/csm_phpmyadmin-tab-structure-schaltflaeche-relation-view_efe78c39bb.webp "phpMyAdmin: Tab „Structure“, Schaltfläche „Relation view“")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-tab-structure-schaltflaeche-relation-view.png) Die Schaltfläche „Relation view“ im Tab „Structure“ (rot hinterlegt)       Fremdschlüsselbeziehungen definieren Sie in der Beziehungsansicht einer Datentabelle über das Eingabefeld „**Foreign key constraints**“:

[![Bild: phpMyAdmin: Eingabemaske „Foreign key constraints“](https://www.ionos.at/digitalguide/fileadmin/_processed_/1/c/csm_phpmyadmin-eingabemaske-foreign-key-constraints_844a057686.webp "phpMyAdmin: Eingabemaske „Foreign key constraints“")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-eingabemaske-foreign-key-constraints.png) Fremdschlüsselbeziehungen definieren Sie über die Eingabemaske „Foreign key contraints“       In der Tabelle *album* soll die Spalte *interpret\_id* als Fremdschlüssel fungieren, der auf dem Primärschlüssel *interpret\_id* der Tabelle *interpret* beruht.

[![Bild: phpMyAdmin: Eingabemaske „Foreign key contraints“ mit Beispielwerten](https://www.ionos.at/digitalguide/fileadmin/_processed_/4/9/csm_phpmyadmin-eingabemaske-foreign-key-constraints-mit-beispielwerten_18b8321b0b.webp "phpMyAdmin: Eingabemaske „Foreign key contraints“ mit Beispielwerten")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-eingabemaske-foreign-key-constraints-mit-beispielwerten.png) Unter „Foreign key constraint (INNODB)“ definieren Sie, auf welchen Primärschlüssel sich der Fremdschlüssel bezieht       Wir wählen somit im Dropdown-Menü unter „Column“ die Spalte *interpret\_id* als Fremdschlüssel aus. Beachten Sie, dass hier nur Spalten aufgeführt werden, die als *INDEX*, *UNIQUE* oder *PRIMARY* markiert wurden. Im dreiteiligen Eingabefeld „Foreign key constraint (InnoDB)“ bestimmen wir, auf welchem Primärschlüssel welcher Tabelle in welcher Datenbank unser Fremdschlüssel beruhen soll. Wir wählen wie folgt:

Datenbank: *test*

Tabelle: *interpret*

Primärschlüssel: *interpret\_id*

Das Feld „Constraint name“ kann leer bleiben. Das Datenbank-Management-System vergibt hier automatisch einen Namen. Definieren müssen Sie jedoch, wie sich eine Tabelle mit Fremdschlüssel verhält, sobald der dem Fremdschlüssel zugrundeliegende Primärschlüssel verändert oder gelöscht wird.

Wird beispielsweise ein Interpret aus der Elterntabelle *interpret* gelöscht, verschwindet auch der mit diesem Eintrag verbundene Primärschlüssel. Es gilt somit zu klären, was mit Einträgen passieren soll, die sich auf diesen Eintrag via Fremdschlüssel beziehen – in unserem Beispiel also Alben eines Interpreten.

Um das Verhalten einer Tabelle mit Fremdschlüssel 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änderung der Elterntabelle aus, sofern andere Tabellen existieren, die auf diese verweisen. In unserem Fall könnte ein Datensatz in der Tabelle *interpret* nicht gelöscht werden, wenn Datensätze in der Tabelle *album* mit diesem verknüpft sind.

- **CASCADE:** Die Option CASCADE sorgt dafür, dass eine Veränderung in der Elterntabelle an alle Tabellen weitergeben wird, die sich auf diese Elterntabelle beziehen. Setzen wir beispielsweise die *interpret\_id* des Interpreten *The Rolling Stones* von 2 auf 8, wird diese Änderung bei der Foreign-Key-Option CASCADE auch für alle Tabellen übernommen, die die *interpret\_id* als Fremdschlüssel nutzen. Wird ein Eintrag in der Elterntabelle gelöscht, führt dies dazu, dass auch alle mit diesem Eintrag verbundenen Datensätze in anderen Tabellen gelöscht werden. Achtung: Auf diese Weise kann das Löschen eines einzelnen Eintrags das Verschwinden zahlreicher Datensätze nach sich ziehen.

- **SET NULL:** Wählen Sie die Option SET NULL, wird der Wert in der Fremdschlüsselspalte auf *NULL* gesetzt, sobald sich der Primärschlüssel in der Elterntabelle ändert oder dieser gelöscht wird.

- **NO ACTION:** In MySQL ist die Option NO ACTION ein Äquivalent der Option RESTRICT.

Haben Sie die gewünschte Option für die Fremdschlüsselbeziehung definiert, bestätigen Sie Ihre Eingabe mit einem Klick auf „Save“. Das Datenbank-Management-System vergibt automatisch einen Namen für die neu angelegte Beziehung.

[![Bild: phpMyAdmin: Definierte Fremdschlüsselbeziehungen](https://www.ionos.at/digitalguide/fileadmin/_processed_/0/0/csm_phpmyadmin-definierte-fremdschluesselbeziehungen_2c0ad73db6.webp "phpMyAdmin: Definierte Fremdschlüsselbeziehungen")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/phpmyadmin-definierte-fremdschluesselbeziehungen.png) Alle definierten Fremdschlüsselbeziehungen werden unter „Foreign key constraints“ im Tab „Structure“ aufgeführt       ### *JOIN*-Typen in MySQL und MariaDB

Fremdschlüsselbeziehungen ermöglichen es Ihnen, Daten aus verschiedenen Tabellen gleichzeitig 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-Management-System nach gemeinsamen Einträgen in beiden via *JOIN* verbunden Tabellen. Es werden nur die Datensätze eingelesen, in denen es Übereinstimmungen gibt, sprich: in denen die Werte in den verknüpfenden Spalten (Primärschlüssel und Fremdschlüssel) in beiden Tabellen übereinstimmen.

- **OUTER JOIN**: Bei einem [OUTER JOIN](https://www.ionos.at/digitalguide/hosting/hosting-technik/sql-outer-join/) unterschiedet man zwischen linker und rechter Datentabelle. Anders als beim *INNER JOIN* werden nicht nur die Datensätze eingelesen, bei denen sich Übereinstimmungen in beiden Tabellen finden, sondern zusätzlich auch alle übrigen Datensätze der rechten bzw. der linken Tabelle.

- **LEFT JOIN:** Es werden alle Datensätze der linken Tabelle eingelesen sowie alle Datensätze der rechten Tabelle, in der sich Übereinstimmungen finden.

- **RIGHT JOIN:** Es werden alle Datensätze der rechten Tabelle eingelesen sowie alle Datensätze der linken Tabelle, in der sich Übereinstimmungen finden.

Im Rahmen unseres MySQL-Einsteiger-Tutorials beschränken wir auf den *INNER JOIN*.

Die Syntax eines INNER JOINs entspricht folgendem Grundschema:

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

Der SQL-Befehl *SELECT* in Kombination mit dem Platzhalter *\** weist das Datenbank-Management-System an, die Werte aller Spalten auszulesen, für die die Bedingungen der *ON*- und der *WHERE*-Klausel gelten.

Da es sich um einen *INNER JOIN* handelt, werden nur die Datensätze, bei denen sich eine Übereinstimmung zwischen dem Fremdschlüssel der *tabelle1* und dem Primärschlüssel der *tabelle2* findet, aus der Datenbank geholt. Darüber hinaus lässt sich mithilfe der *WHERE*-Klausel eine **optionale Filterfunktion** definieren.

Wir verdeutlichen dies an einem Beispiel, das sich auf unsere normalisierten Tabellen album, interpret und title bezieht:

```none
<?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 Beispielskript zeigt einen *INNER JOIN*, bei dem die Tabelle *album* mit der Tabelle *interpret* verbunden wird. Wir lesen lediglich die Datensätze aus, bei denen eine Übereinstimmung zwischen Primärschlüssel und Fremdschlüssel besteht.

```none
album.interpret_id = interpret.interpret_id
```

In unserem Datenbestand sind das alle Datensätze (ein *LEFT* oder *RIGHT JOIN* hätte daher das gleiche Ergebnis gehabt). Im Anschluss geben wir die eingelesenen Werte mithilfe einer *foreach*-Schleife und dem Sprachkonstrukt *echo* im Browser aus.

[![Bild: Codebeispiel INNER JOIN](https://www.ionos.at/digitalguide/fileadmin/_processed_/d/a/csm_codebeispiel-inner-join_7c5b64f4ec.webp "Codebeispiel INNER JOIN")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/codebeispiel-inner-join.png) Das Beispielskript gibt uns alle in der Datenbank hinterlegten Alben inklusive Interpret und Jahr der Erstveröffentlichung aus       Im Rahmen eines SQL-Statements haben wir somit die Interpretenangabe aus der Tabelle *interpret* und die Angaben zum Albumtitel und zum Erscheinungsjahr der Platte aus der Tabelle *album* ausgelesen.

Welche Datensätze des Joins ausgegeben werden, lässt sich mit einer Bedingung in der WHERE-Klausel einschränken. Möchten wir beispielsweise lediglich die Alben ausgeben, die im Jahr 1968 erschienen sind, können wir folgendermaßen vorgehen:

```none
<?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 überschaubaren Datenbank, das im Jahr 1968 veröffentlicht wurde.

[![Bild: Codebeispiel: INNER JOIN mit Bedingung](https://www.ionos.at/digitalguide/fileadmin/_processed_/3/1/csm_coebeispiel-inner-jon-mit-bedingung_d60248c150.webp "Codebeispiel: INNER JOIN mit Bedingung")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/coebeispiel-inner-jon-mit-bedingung.png) Mit der Bedingung in der WHERE-Klausel schränken wir die Ausgabe auf einen Datensatz ein       Mithilfe des *JOIN*-Befehls lassen sich beliebig viele Tabellen zur einen Datenverbund zusammenschließen. Im folgenden Beispiel verknüpfen wir die Tabelle *album* in einem INNER JOIN mit den Tabellen *interpret* und *title*, um uns sämtliche Informationen zu den in der Datenbank gespeicherten Musiktiteln ausgeben zu lassen.

```none
<?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 />";
}
?>
```

[![Bild: INNER JOIN über drei Tabellen](https://www.ionos.at/digitalguide/fileadmin/_processed_/b/2/csm_inner-join-ueber-drei-tabellen_fb82da7ed0.webp "INNER JOIN über drei Tabellen")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/inner-join-ueber-drei-tabellen.png) Mit dem SQL-Befehl JOIN verbinden Sie beliebig viele Datentabellen     Auch hier können wir nach Bedarf eine *WHERE*-Klausel mit einer Filterfunktion definieren. Beispielsweise, wenn wir lediglich die Informationen zu Track 7 auf dem Album „Abbey Road“ ausgeben möchten.

```none
<?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 />";
}
?>
```

[![Bild: INNER JOIN über drei Tabellen mit Bedingung](https://www.ionos.at/digitalguide/fileadmin/_processed_/3/8/csm_inner-join-ueber-drei-tabellen-mit-bedingung_14f4ee60b6.webp "INNER JOIN über drei Tabellen mit Bedingung")](https://www.ionos.at/digitalguide/fileadmin/DigitalGuide/Screenshots/inner-join-ueber-drei-tabellen-mit-bedingung.png) INNER JOIN mit zwei durch ein logisches AND verknüpften Bedingungen     Beachten Sie, dass wir es bei der Tabelle *title* mit einem **mehrteiligen Primärschlüssel** zu tun haben. Möchten wir auf einen bestimmten Titel verweisen, benötigen wir neben der Track-Nummer die *album\_id*, die zusammen mit dem Titel des Albums in der Tabelle *album* hinterlegt ist.

## Vom Einsteiger zum Profi

Unser MySQL-Tutorial für Einsteiger versteht sich als Crashkurs, der Ihnen die **Grundlagen SQL-basierter Datenbanksysteme** näherbringt und an praxisrelevanten Beispielen einfache Datenbankoperationen veranschaulicht. Sollte Ihr Interesse über die hier dargestellten Anwendungsmöglichkeiten hinausgehen, empfehlen wir die bereits im Einführungskapitel verlinkten Dokumentationen der DBMS MySQL und MariaDB. Außerdem finden Sie im Internet zahlreiche Websites, die Tutorials und Anwendungsbeispiele zum beliebten Datenbank-Management-System anbieten. Zu empfehlen ist zudem die Internetplattform [**Stack Overflow**](http://stackoverflow.com), auf der sich eine rege Nutzercommunity von mehr als 6,5 Millionen Entwicklern über aktuelle Fragen und Problemstellungen der Software-Entwicklung austauscht. Und natürlich stehen Ihnen auch hier im IONOS Digital Guide viele weiterführende Artikel zum Thema Datenbanken zur Verfügung – Sie finden sie über die unten aufgeführten Tags.


This is a markdown version of: [https://www.ionos.at/digitalguide/server/knowhow/mysql-lernen-leicht-gemacht/](https://www.ionos.at/digitalguide/server/knowhow/mysql-lernen-leicht-gemacht/) for AI/LLM consumption.