Lernen / Cheat Sheet
SQL Cheat Sheet für Tester
Das SQL, mit dem Tester die Daten hinter einer UI oder API prüfen: den Datensatz finden, kontrollieren, ob er sich geändert hat, und Duplikate, verwaiste Datensätze und NULLs aufspüren, die nicht da sein sollten. Die Syntax ist Standard-SQL und läuft auf PostgreSQL, MySQL 8 und SQLite. Wo sich eine Datenbank anders verhält, steht es in der Anmerkung.
Geprüft mit Standard-SQL; geprüft mit PostgreSQL 16, MySQL 8 und SQLite 3
Zeilen finden
| Aufgabe | Code |
|---|---|
| Spalten aus einer Tabelle | SELECT id, email FROM users; |
| Filtern | SELECT * FROM orders WHERE status = 'paid' AND total > 100; |
| Einer von mehreren Werten | WHERE status IN ('paid', 'shipped') |
| Bereich (beide Grenzen inklusive) | WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31' |
| Muster | WHERE email LIKE '%@example.com' |
| Fehlender Wert | WHERE phone IS NULL |
| Sortieren, neueste zuerst | ORDER BY created_at DESC |
| Erste 10 Zeilen | LIMIT 10 |
| Eindeutige Werte | SELECT DISTINCT status FROM orders; |
SQL Server verwendet SELECT TOP 10 statt LIMIT. BETWEEN auf Timestamps schließt vom Enddatum nur Mitternacht ein; für ganze Tage nehmen Sie < Folgetag.
Aggregieren
| Aufgabe | Code |
|---|---|
| Zeilen zählen | SELECT COUNT(*) FROM orders; |
| Nicht-NULL-Werte zählen | SELECT COUNT(shipped_at) FROM orders; |
| Eindeutige Werte zählen | SELECT COUNT(DISTINCT user_id) FROM orders; |
| Summen pro Gruppe | SELECT status, COUNT(*), SUM(total) FROM orders GROUP BY status; |
| Gruppen filtern | SELECT user_id, COUNT(*) FROM orders GROUP BY user_id HAVING COUNT(*) > 5; |
| Minimum, Maximum, Durchschnitt | SELECT MIN(total), MAX(total), AVG(total) FROM orders; |
WHERE filtert Zeilen vor dem Gruppieren; HAVING filtert danach die Gruppen.
Join
| Aufgabe | Code |
|---|---|
| Nur passende Zeilen | SELECT o.id, u.email FROM orders o JOIN users u ON u.id = o.user_id; |
| Alle Zeilen der linken Tabelle behalten | SELECT u.email, o.id FROM users u LEFT JOIN orders o ON o.user_id = u.id; |
| Zeilen ohne Treffer (Anti-Join) | SELECT u.* FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.id IS NULL; |
| Dasselbe mit NOT EXISTS | SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id); |
Ein Join über einen 1:n-Schlüssel wiederholt die „1“-Seite. Wirkt SUM() nach einem Join zu groß, hat sich der Join vervielfacht (Fan-out).
Datenprüfungen für Tester
| Aufgabe | Code |
|---|---|
| Duplikate | SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1; |
| Verwaiste Datensätze (Kind ohne Elternteil) | SELECT o.* FROM orders o LEFT JOIN users u ON u.id = o.user_id WHERE u.id IS NULL; |
| Pflichtfeld leer | SELECT * FROM users WHERE email IS NULL OR TRIM(email) = ''; |
| Werte außerhalb des Bereichs | SELECT * FROM order_items WHERE quantity <= 0 OR unit_price < 0; |
| Kopfsumme ≠ Positionen | SELECT o.id FROM orders o JOIN order_items i ON i.order_id = o.id GROUP BY o.id, o.total HAVING o.total <> SUM(i.quantity * i.unit_price); |
| Zeile existiert nach einer UI-Aktion | SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 1; |
NULL-Fallen
| Falle | Was passiert |
|---|---|
| WHERE col = NULL | Nie wahr. Verwenden Sie IS NULL / IS NOT NULL. |
| WHERE status <> 'paid' | Überspringt Zeilen, in denen status NULL ist. Ergänzen Sie OR status IS NULL, wenn Sie diese brauchen. |
| NOT IN (Subquery, die ein NULL liefert) | Liefert überhaupt keine Zeilen. Verwenden Sie stattdessen NOT EXISTS. |
| COUNT(col) vs. COUNT(*) | COUNT(col) überspringt NULLs, COUNT(*) zählt jede Zeile. |
| Arithmetik mit NULL | price + NULL ergibt NULL. Mit COALESCE(col, 0) umschließen, wenn 0 der richtige Standardwert ist. |
Window Functions
| Aufgabe | Code |
|---|---|
| Zeilen pro Gruppe nummerieren | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) |
| Neueste Zeile pro Gruppe | SELECT * FROM (SELECT o.*, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM orders o) t WHERE rn = 1; |
| Rang mit Gleichständen | RANK() OVER (ORDER BY total DESC) / DENSE_RANK() … |
| Wert der vorherigen Zeile | LAG(status) OVER (PARTITION BY test_name ORDER BY run_at) |
| Laufende Summe | SUM(total) OVER (ORDER BY created_at) |
Unterstützt in PostgreSQL, MySQL 8+, SQLite 3.25+ und SQL Server. Eine Window Function ist in WHERE nicht erlaubt; filtern Sie in einer äußeren Abfrage.
Daten sicher ändern
| Aufgabe | Code |
|---|---|
| Einfügen | INSERT INTO users (email, name) VALUES ('qa+1@example.com', 'QA One'); |
| Aktualisieren (immer mit WHERE) | UPDATE orders SET status = 'cancelled' WHERE id = 1043; |
| Löschen (immer mit WHERE) | DELETE FROM sessions WHERE user_id = 42; |
| Ausprobieren, dann rückgängig machen | BEGIN; UPDATE orders SET status = 'cancelled' WHERE id = 1043; SELECT status FROM orders WHERE id = 1043; ROLLBACK; |
Führen Sie dasselbe WHERE zuerst als SELECT aus und prüfen Sie die Zeilenanzahl, bevor Sie UPDATE oder DELETE ausführen.