- Veröffentlicht am
Was ist SQL? Zweck, Ursprung und Verwendung verstehen
- Autoren
Was ist SQL? Zweck, Ursprung und Verwendung verstehen
Die meisten modernen Anwendungen, von einfachen Blogs bis zu riesigen sozialen Netzwerken, speichern und lesen Daten mit einer Sprache namens SQL.
In diesem Artikel erfährst du:
- Was SQL ist und warum es entwickelt wurde
- Wie relationale Datenbanken Informationen organisieren
- Ein konkretes Beispiel mit zwei Tabellen
- Wie du echte SQL-Abfragen einschließlich
JOINs ausführst - Warum SQL nach über 50 Jahren noch immer der Standard ist
Legen wir los.
Was ist SQL?
SQL (Structured Query Language) ist eine deklarative Programmiersprache zur Verwaltung relationaler Datenbanken, also Datenbanken, die auf Tabellen, Zeilen und deren Beziehungen aufgebaut sind.
Mit SQL kannst du:
- Tabellen erstellen
- Daten einfügen und ändern
- Daten mit Filtern abfragen
- Zusammengehörige Daten aus mehreren Tabellen verknüpfen
- Bedingungen durchsetzen, etwa eindeutige E-Mail-Adressen oder Fremdschlüssel
SQL wird in vielen beliebten relationalen Datenbanksystemen verwendet. Hier einige Beispiele:
- PostgreSQL: Fortschrittliche relationale Open-Source-Datenbank mit umfassender Unterstützung der SQL-Standards.
- MySQL: In Webanwendungen weit verbreitet, bekannt für Geschwindigkeit und Einfachheit.
- MariaDB: Von der Community entwickelte Abspaltung von MySQL mit erweiterten Leistungsfunktionen.
- SQLite: Eine leichtgewichtige, dateibasierte Datenbank, die in mobilen Anwendungen, Browsern und eingebetteten Systemen verwendet wird.
- Microsoft SQL Server: Kommerzielle Datenbank auf Unternehmensniveau mit leistungsfähigen Werkzeugen für Windows- und Cloud-Umgebungen.
- Vollständig verwaltete SQL-Dienste in der Cloud, zum Beispiel:
- Amazon RDS
- Google Cloud SQL
- PlanetScale
Diese Plattformen (Amazon RDS, Google Cloud SQL, PlanetScale) übernehmen Sicherungen, Skalierung, Aktualisierungen und Wartung und bieten dir trotzdem die Leistungsfähigkeit von SQL.
Warum SQL entwickelt wurde
In den 1970er-Jahren arbeiteten die Forscher Edgar F. Codd und Donald Chamberlin bei IBM an einer besseren Möglichkeit, Daten zu speichern und abzurufen. Damals waren Datenbanken hierarchisch oder netzwerkbasiert. Der Umgang mit ihnen war:
- Komplex
- Fehleranfällig
- Eng an die physische Speicherung gekoppelt
- Schwer flexibel abzufragen
Um das zu lösen, führten sie das relationale Modell ein, das Daten in Tabellen mit klar definierten Beziehungen organisiert.
SQL wurde entwickelt, weil:
- Entwickler eine einfache, lesbare Sprache zum Abfragen von Daten benötigten.
- Unternehmen einheitliche Regeln für die Speicherung von Beziehungen benötigten.
- Daten ohne Kenntnis interner Speicherdetails zugänglich sein mussten.
Das Ergebnis?
SQL wurde 1986 zum Standard (ANSI SQL), und nahezu jede wichtige Datenbank basiert auch heute noch darauf.
Tabellen, Spalten und Beziehungen verstehen
In einer relationalen Datenbank:
- Ist eine Tabelle wie ein Tabellenblatt aufgebaut (Zeilen + Spalten)
- Stellt eine Zeile einen einzelnen Datensatz dar
- Stellt eine Spalte eine Eigenschaft dar
- Identifiziert ein Primärschlüssel jede Zeile eindeutig
- Verknüpft ein Fremdschlüssel eine Tabelle mit einer anderen
Sehen wir uns ein einfaches Beispiel mit Benutzern und Bestellungen an.
Beispielschema: Benutzer und Bestellungen
Tabelle 1: users
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
Tabelle 2: orders
| id | user_id | product | price |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
Beziehung
- Jede Bestellung gehört zu einem Benutzer (
orders.user_id → users.id). - Ein Benutzer kann mehrere Bestellungen haben (1:n-Beziehung).
Die Tabellen erstellen (SQL-Beispiel)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
product TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL
);
Damit werden zwei stark typisierte relationale Tabellen mit einer Fremdschlüsselbedingung erstellt. Doch was bedeutet „stark typisiert“? In SQL-Datenbanken wie PostgreSQL und MySQL hat jede Spalte einen strikten Datentyp:
- Zeichenketten müssen Zeichenketten sein
- Zahlen müssen Zahlen sein
- Datumswerte müssen Datumswerte sein
- Falsche Typen verursachen Fehler
Das verhindert Fehler wie:
- „Zwanzig Dollar“ in einer numerischen Spalte speichern
- 123 in einem E-Mail-Feld speichern
- Ungültige Datumswerte speichern
SQLite ist anders: Es ist typflexibel („dynamische Typisierung“), erzwingt Typen also weniger streng. PostgreSQL ist unter den großen Datenbanken am stärksten typisiert.
Tabellenerstellung Zeile für Zeile erklärt
Tabelle 1: users
CREATE TABLE users (
Erklärung: Damit beginnt die Definition einer neuen Tabelle namens users.
- Funktioniert in: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server
- „Tabelle“ = strukturierte Menge von Zeilen und Spalten in der Datenbank.
id SERIAL PRIMARY KEY,
Erklärung:
id → Der Spaltenname.
SERIAL → Automatisch hochgezählte Ganzzahl. PostgreSQL-spezifisch.
Entsprechungen in anderen Engines:
- MySQL:
INT AUTO_INCREMENT - SQLite:
INTEGER PRIMARY KEY AUTOINCREMENT
- MySQL:
PRIMARY KEY → Gewährleistet Eindeutigkeit und identifiziert jede Zeile.
name TEXT NOT NULL,
Erklärung:
- name → Spalte zum Speichern von Text.
- TEXT → Text variabler Länge (Zeichenkette).
- NOT NULL → Darf nicht NULL sein.
Funktioniert in: PostgreSQL, MySQL, MariaDB, SQLite (SQL Server verwendet jedoch VARCHAR(MAX) statt TEXT).
email TEXT UNIQUE NOT NULL
Erklärung:
- email → Speichert die E-Mail-Adresse des Benutzers.
- TEXT → Texttyp.
- UNIQUE → Verhindert doppelte E-Mail-Adressen.
- NOT NULL → Pflichtfeld.
Funktioniert in: PostgreSQL, MySQL, MariaDB, SQLite (unter Berücksichtigung von Unterschieden bei den Interna des TEXT-Typs).
);
Erklärung: Schließt die Tabellendefinition ab.
Tabelle 2: orders
CREATE TABLE orders (
Beginnt eine neue Tabelle namens orders. Funktioniert in allen wichtigen Engines.
id SERIAL PRIMARY KEY,
Gleiche Bedeutung wie in der Tabelle users:
- Automatisch hochgezählter Primärschlüssel
- PostgreSQL-spezifischer Typ (
SERIAL) - MySQL verwendet
INT AUTO_INCREMENT - SQLite verwendet
INTEGER PRIMARY KEY AUTOINCREMENT
user_id INTEGER NOT NULL REFERENCES users(id),
Erklärung:
- user_id → Eine Zahl, die die Bestellung mit einer Zeile in
usersverknüpft. - INTEGER → Ganzzahl.
- NOT NULL → Jede Bestellung muss zu einem Benutzer gehören.
- REFERENCES users(id) → Fremdschlüsselbedingung.
Bedeutung von Fremdschlüssel:
- Stellt sicher, dass
user_idmit einer tatsächlich vorhandenenidin der Tabelleusersübereinstimmen muss. - Wird ein Benutzer gelöscht, setzt die Datenbank die Beziehung gemäß den Fremdschlüsselregeln durch.
Engine-Kompatibilität:
- PostgreSQL: Vollständig unterstützt.
- MySQL/MariaDB: Funktioniert, erfordert aber die Speicher-Engine InnoDB.
- SQLite: Nur bei aktivierter Fremdschlüsselunterstützung unterstützt (
PRAGMA foreign_keys = ON;).
product TEXT NOT NULL,
Speichert den Namen des gekauften Produkts.
- Typ TEXT → Funktioniert je nach Engine unterschiedlich, speichert aber immer Zeichenketten.
- Pflichtfeld (
NOT NULL).
price NUMERIC(10,2) NOT NULL
Erklärung:
price → Der Preis des Produkts.
NUMERIC(10,2) → Zahl mit fester Präzision mit:
- Bis zu 10 Stellen insgesamt
- 2 Nachkommastellen
Das ist ideal für Geldbeträge, weil es Gleitkommafehler vermeidet.
Engine-Kompatibilität:
- PostgreSQL: Vollständige Unterstützung.
- MySQL:
DECIMAL(10,2)ist gleichwertig. - SQLite: Akzeptiert es, speichert intern jedoch als TEXT, INT oder REAL (dynamische Typisierung).
);
Beendet die Tabelle.
Die Beispieldaten einfügen
Nachdem unsere Tabellen erstellt sind, fügen wir die zuvor gezeigten Beispieldaten ein. Wir geben keine id-Werte manuell an. Die Datenbank generiert sie mit dem Typ SERIAL (automatische Hochzählung) automatisch.
Die users-Daten einfügen
INSERT INTO users (name, email)
VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Caroline', 'caroline@example.com');
Einfügen in users Zeile für Zeile erklärt
INSERT INTO users (name, email)
INSERT INTO usersWir fügen der Tabelle users neue Zeilen hinzu.(name, email)Wir geben nurnameundemailan, nichtid.Warum geben wir die ID nicht an? Weil die Spalte
ideinen Serial-/Auto-Increment-Typ (SERIALvon PostgreSQL) verwendet, erzeugt die Datenbank IDs automatisch:Erster Benutzer →
id = 1Zweiter Benutzer →
id = 2Dritter Benutzer →
id = 3
Das ist in PostgreSQL, MySQL, MariaDB und SQLite vorhersehbar (mit ihren jeweiligen Mechanismen zur automatischen Hochzählung).
VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Caroline', 'caroline@example.com');
VALUESleitet eine Liste einzufügender Zeilen ein.- Jede
( ... )-Gruppe entspricht einer neuen Zeile. - Die Datenbank weist IDs automatisch in der Reihenfolge zu, in der diese Zeilen erscheinen.
Die resultierende Tabelle users nach dem Einfügen:
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
Obwohl wir keine IDs manuell angegeben haben, gewährleistet die automatische Hochzählung diese Reihenfolge.
Die orders-Daten einfügen
Zum Einfügen von Bestellungen in orders müssen wir user_id sehr wohl angeben, weil eine Bestellung zu einem bestimmten Benutzer gehören muss.
Obwohl wir die Benutzer-IDs nicht manuell festgelegt haben, wissen wir trotzdem, welche sie sind:
- Alice → ID 1
- Bob → ID 2
- Caroline → ID 3
Sie wurden automatisch in der Einfügereihenfolge erzeugt. Die automatische Hochzählung weist IDs immer fortlaufend zu, solange keine Zeilen gelöscht werden.
Hier die Einfügeanweisung:
INSERT INTO orders (user_id, product, price)
VALUES
(1, 'Coffee Mug', 12.99),
(1, 'Hoodie', 39.99),
(2, 'Stickers', 4.99);
Einfügen in orders Zeile für Zeile erklärt
INSERT INTO orders (user_id, product, price)
Wir fügen neue Zeilen in die Tabelle orders ein.
Wir geben Folgendes an:
user_id→ Zu welchem Benutzer die Bestellung gehörtproduct→ Nameprice→ Preis
Beachte erneut: Wir geben id (orders.id) nicht an, weil der Wert automatisch hochgezählt wird.
VALUES
(1, 'Coffee Mug', 12.99),
(1, 'Hoodie', 39.99),
(2, 'Stickers', 4.99);
(1, 'Coffee Mug', 12.99)- Diese Bestellung gehört zu Benutzer 1 (Alice).
(1, 'Hoodie', 39.99)- Eine weitere Bestellung für Alice.
(2, 'Stickers', 4.99)- Bestellung für Benutzer 2 (Bob).
Woher wissen wir, welcher Benutzer welche ID hat? Das lässt sich so erklären:
Die Benutzer wurden zuerst eingefügt.
Die automatische Hochzählung weist fortlaufende Ganzzahlen zu.
Daher:
- Erster eingefügter Benutzer → ID 1
- Zweiter eingefügter Benutzer → ID 2
- Dritter eingefügter Benutzer → ID 3
Damit ist die Referenzierung von Benutzern über ihre ID vollständig vorhersehbar.
Die endgültigen Tabellen
users
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
orders
| id | user_id | product | price |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
Daten mit SQL abfragen
Hier sind SQL-Abfragen, die wir auf den beiden zuvor definierten Tabellen ausführen können.
1. Alle Benutzer auswählen
SELECT * FROM users;
Ergebnis:
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
2. Benutzer mit einem Filter auswählen
SELECT * FROM users
WHERE email LIKE '%alice@example.com';
Was das bedeutet:
LIKEist ein SQL-Operator, mit dem Text anhand von Mustern verglichen wird.%ist ein Platzhalter, der beliebig viele Zeichen, einschließlich null Zeichen, abdeckt.
Also:
'%alice@example.com'bedeutet: „Finde jeden Text, der mitalice@example.comendet.“
Dies liefert die Zeile zurück, in der die E-Mail-Adresse des Benutzers genau Folgendes ist:
alice@example.com
Es würde aber auch Werte wie diese finden:
test-alice@example.comhello.alice@example.comanythingalice@example.com
Dies gilt, sofern diese Werte vorhanden sind.
Ergebnis:
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
Diese Abfrage liefert nur den Benutzer zurück, dessen E-Mail-Adresse dem Filter entspricht.
3. Eine neue Bestellung einfügen
INSERT INTO orders (user_id, product, price)
VALUES (2, 'T-Shirt', 29.99);
Die resultierende Tabelle orders nach dem Einfügen:
| id | user_id | product | price |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
| 4 | 2 | T-Shirt | 29.99 |
- Die neue Zeile hat die automatisch generierte
id = 4.user_id = 2verknüpft diese Bestellung mit Bob.
JOINs: Ein leistungsfähiger Teil von SQL
Mit JOINs kannst du zusammengehörige Daten über mehrere Tabellen hinweg abfragen.
Sehen wir sie uns anhand unserer Tabellen users und orders an.
Beispiel 1: INNER JOIN (am häufigsten verwendet)
Rufe jede Bestellung mit dem Benutzer ab, der sie aufgegeben hat.
SELECT users.name, orders.product, orders.price
FROM orders
INNER JOIN users
ON orders.user_id = users.id;
Ergebnis:
| name | product | price |
|---|---|---|
| Alice | Coffee Mug | 12.99 |
| Alice | Hoodie | 39.99 |
| Bob | Stickers | 4.99 |
| Bob | T-Shirt | 29.99 |
Bob erscheint jetzt zweimal, wegen der zuvor eingefügten neuen Bestellung:
INSERT INTO orders (user_id, product, price)
VALUES (2, 'T-Shirt', 29.99);
Beispiel 2: LEFT JOIN
Rufe alle Benutzer ab, auch diejenigen ohne Bestellungen.
SELECT users.name, orders.product
FROM users
LEFT JOIN orders
ON users.id = orders.user_id;
Ergebnis:
| name | product |
|---|---|
| Alice | Coffee Mug |
| Alice | Hoodie |
| Bob | Stickers |
| Bob | T-Shirt |
| Caroline | null |
Caroline erscheint mit null, weil sie keine Bestellungen hat.
Beispiel 3: Aggregatabfrage
Der von jedem Benutzer insgesamt ausgegebene Betrag:
SELECT users.name, SUM(orders.price) AS total_spent
FROM users
LEFT JOIN orders ON users.id = orders.user_id
GROUP BY users.name;
Ergebnis:
| name | total_spent |
|---|---|
| Alice | 52.98 |
| Bob | 34.98 |
| Caroline | null |
Erklärung
- Alice: 12.99 + 39.99 = 52.98
- Bob: 4.99 + 29.99 = 34.98
- Caroline: Keine Bestellungen → NULL
Wo du mehr erfahren kannst
Abschließende Gedanken
SQL gibt es seit über 50 Jahren, und trotzdem bildet es noch immer die Grundlage der wichtigsten Anwendungen der Welt, weil es:
- Deklarativ ist
- Einheitlich ist
- Vorhersehbar ist
- Skalierbar ist
- Auf jahrzehntelanger Forschung basiert
Sobald du verstehst, wie Tabellen und Joins funktionieren, wird SQL zu einem der leistungsfähigsten Werkzeuge in deinem Programmierwerkzeugkasten.