SQL (Structured Query Language) ist die Standardsprache für relationale Datenbanken. Diese Grundlagen gelten für MySQL, MariaDB, PostgreSQL und andere.

Datenbankverbindung

MySQL/MariaDB

mysql -u username -p datenbankname

PostgreSQL

psql -U username -d datenbankname

SELECT - Daten abfragen

Grundlegende Abfragen

-- Alle Spalten
SELECT * FROM users;

-- Bestimmte Spalten
SELECT id, name, email FROM users;

-- Mit Alias
SELECT name AS benutzername, email AS mailadresse FROM users;

WHERE - Filtern

-- Einfache Bedingung
SELECT * FROM users WHERE active = 1;

-- Mehrere Bedingungen
SELECT * FROM users WHERE active = 1 AND age >= 18;
SELECT * FROM users WHERE city = 'Berlin' OR city = 'Hamburg';

-- Vergleichsoperatoren
SELECT * FROM products WHERE price > 100;
SELECT * FROM products WHERE price >= 50 AND price <= 100;
SELECT * FROM products WHERE price BETWEEN 50 AND 100;

-- NULL-Werte
SELECT * FROM users WHERE phone IS NULL;
SELECT * FROM users WHERE phone IS NOT NULL;

LIKE - Textsuche

-- Beginnt mit
SELECT * FROM users WHERE name LIKE 'Max%';

-- Endet mit
SELECT * FROM users WHERE email LIKE '%@gmail.com';

-- Enthält
SELECT * FROM users WHERE name LIKE '%mueller%';

-- Ein einzelnes Zeichen
SELECT * FROM users WHERE name LIKE 'M_yer';

IN - Mehrere Werte

SELECT * FROM users
WHERE country IN ('Deutschland', 'Österreich', 'Schweiz');

ORDER BY - Sortieren

-- Aufsteigend (Standard)
SELECT * FROM users ORDER BY name;
SELECT * FROM users ORDER BY name ASC;

-- Absteigend
SELECT * FROM users ORDER BY created_at DESC;

-- Mehrere Spalten
SELECT * FROM users ORDER BY country, city, name;

LIMIT - Anzahl begrenzen

-- Erste 10 Ergebnisse
SELECT * FROM users LIMIT 10;

-- 10 Ergebnisse ab Position 20 (Pagination)
SELECT * FROM users LIMIT 10 OFFSET 20;
-- MySQL Kurzform:
SELECT * FROM users LIMIT 20, 10;

DISTINCT - Eindeutige Werte

SELECT DISTINCT country FROM users;
SELECT DISTINCT country, city FROM users;

INSERT - Daten einfügen

Einzelner Datensatz

-- Alle Spalten (nicht empfohlen)
INSERT INTO users VALUES (1, 'Max', 'max@example.com');

-- Mit Spaltennamen (empfohlen)
INSERT INTO users (name, email) VALUES ('Max', 'max@example.com');

-- Mehrere Spalten
INSERT INTO products (name, price, category_id, stock)
VALUES ('Laptop', 999.99, 1, 50);

Mehrere Datensätze

INSERT INTO users (name, email) VALUES
    ('Max', 'max@example.com'),
    ('Anna', 'anna@example.com'),
    ('Peter', 'peter@example.com');

INSERT mit SELECT

-- Daten aus anderer Tabelle kopieren
INSERT INTO archive_users (id, name, email)
SELECT id, name, email FROM users WHERE active = 0;

UPDATE - Daten aktualisieren

Grundlegendes Update

-- IMMER mit WHERE!
UPDATE users SET active = 0 WHERE id = 5;

-- Mehrere Spalten
UPDATE users SET name = 'Maximilian', email = 'max@neu.de' WHERE id = 5;

Berechnungen

-- Wert erhöhen
UPDATE products SET stock = stock - 1 WHERE id = 10;
UPDATE products SET price = price * 1.1; -- 10% Erhöhung (ALLE!)

-- Bedingte Aktualisierung
UPDATE products SET stock = stock - 1 WHERE id = 10 AND stock > 0;

WICHTIG: WHERE nicht vergessen!

-- GEFÄHRLICH: Aktualisiert ALLE Datensätze!
UPDATE users SET active = 0;

-- Sicher: Nur bestimmte Datensätze
UPDATE users SET active = 0 WHERE last_login < '2024-01-01';

DELETE - Daten löschen

Datensätze löschen

-- IMMER mit WHERE!
DELETE FROM users WHERE id = 5;

-- Mit Bedingung
DELETE FROM users WHERE active = 0 AND created_at < '2023-01-01';

WICHTIG: WHERE nicht vergessen!

-- GEFÄHRLICH: Löscht ALLE Datensätze!
DELETE FROM users;

-- Sicher: Nur bestimmte Datensätze
DELETE FROM users WHERE id = 5;

TRUNCATE - Alle Daten löschen

-- Schneller als DELETE, setzt Auto-Increment zurück
TRUNCATE TABLE old_logs;

Aggregatfunktionen

Zählen, Summe, Durchschnitt

-- Anzahl
SELECT COUNT(*) FROM users;
SELECT COUNT(*) FROM users WHERE active = 1;
SELECT COUNT(DISTINCT country) FROM users;

-- Summe
SELECT SUM(price) FROM orders WHERE user_id = 5;

-- Durchschnitt
SELECT AVG(price) FROM products;

-- Minimum/Maximum
SELECT MIN(price), MAX(price) FROM products;

GROUP BY - Gruppieren

-- Anzahl pro Kategorie
SELECT category_id, COUNT(*)
FROM products
GROUP BY category_id;

-- Mit Spaltennamen
SELECT c.name, COUNT(p.id) as product_count
FROM categories c
LEFT JOIN products p ON c.id = p.category_id
GROUP BY c.id, c.name;

HAVING - Gruppen filtern

-- Kategorien mit mehr als 10 Produkten
SELECT category_id, COUNT(*) as count
FROM products
GROUP BY category_id
HAVING COUNT(*) > 10;

JOINs - Tabellen verknüpfen

INNER JOIN

-- Nur übereinstimmende Datensätze
SELECT users.name, orders.total
FROM users
INNER JOIN orders ON users.id = orders.user_id;

LEFT JOIN

-- Alle Users, auch ohne Orders
SELECT users.name, orders.total
FROM users
LEFT JOIN orders ON users.id = orders.user_id;

RIGHT JOIN

-- Alle Orders, auch ohne User
SELECT users.name, orders.total
FROM users
RIGHT JOIN orders ON users.id = orders.user_id;

Mehrere JOINs

SELECT
    u.name,
    o.id as order_id,
    p.name as product_name,
    oi.quantity
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN order_items oi ON o.id = oi.order_id
INNER JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= '2024-01-01';

Subqueries

-- In WHERE
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE total > 100);

-- In SELECT
SELECT
    name,
    (SELECT COUNT(*) FROM orders WHERE user_id = users.id) as order_count
FROM users;

-- In FROM
SELECT avg_price
FROM (SELECT AVG(price) as avg_price FROM products) as subquery;

Praktische Beispiele

Top 10 Kunden nach Umsatz

SELECT
    u.name,
    SUM(o.total) as total_spent
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC
LIMIT 10;

Produkte die noch nie verkauft wurden

SELECT p.*
FROM products p
LEFT JOIN order_items oi ON p.id = oi.product_id
WHERE oi.id IS NULL;

Umsatz pro Monat

SELECT
    DATE_FORMAT(created_at, '%Y-%m') as month,
    SUM(total) as revenue
FROM orders
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY month;

Benutzer mit letztem Login

SELECT
    name,
    email,
    DATEDIFF(NOW(), last_login) as days_since_login
FROM users
WHERE last_login < DATE_SUB(NOW(), INTERVAL 30 DAY);

Tipps

Performance

-- Index nutzen (WHERE auf indexierte Spalten)
SELECT * FROM users WHERE email = 'test@example.com';

-- EXPLAIN zeigt Query-Plan
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';

-- LIKE mit Wildcard am Anfang ist langsam
SELECT * FROM users WHERE name LIKE '%mueller%'; -- Langsam
SELECT * FROM users WHERE name LIKE 'mueller%';  -- Schneller

Sicherheit

-- NIEMALS User-Input direkt einsetzen!
-- Falsch (SQL-Injection möglich):
"SELECT * FROM users WHERE name = '" + userName + "'"

-- Richtig: Prepared Statements verwenden
-- PHP PDO:
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->execute([$userName]);

Fazit

Die Grundbefehle SELECT, INSERT, UPDATE und DELETE decken den größten Teil der täglichen Datenbankarbeit ab. Achten Sie besonders bei UPDATE und DELETE auf die WHERE-Klausel. JOINs sind wichtig für relationale Daten, und Aggregatfunktionen ermöglichen aussagekräftige Auswertungen. Für produktiven Einsatz sollten Sie immer Prepared Statements verwenden, um SQL-Injection zu verhindern.