SQL Beispiele für PostgreSQL, MySQL und SQLite
SQL ist die Abfragesprache für relationale Datenbanken; die Schreibweise unterscheidet sich je nach System. Die Snippets bauen ein kleines Shop-Schema aus Kunden, Produkten und Bestellungen auf und zeigen dazu INSERT, UPSERT, JOIN, CTE, Paginierung, Indizes und Datumsfunktionen für PostgreSQL, MySQL und SQLite. Die Hinweise nennen, ob ein Snippet ausgeführt oder nur nach der Referenz geschrieben wurde.
48 Varianten auf einer Seite, jede mit eigenem Kopier- oder Download-Knopf. Darunter: 9× Fiktiv, 2× Absichtlich ungültig.
SQL PostgreSQL: CREATE TABLE kunden
CREATE TABLE kunden ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, kundennummer TEXT NOT NULL UNIQUE, …
Auf PostgreSQL 17 ausgeführt. Geld als NUMERIC, nicht als float.
SQL PostgreSQL: CREATE TABLE produkte
CREATE TABLE produkte ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sku TEXT NOT NULL UNIQUE, …
Auf PostgreSQL 17 ausgeführt. Geld als NUMERIC, nicht als float.
SQL PostgreSQL: CREATE TABLE bestellungen
CREATE TABLE bestellungen ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, kunde_id BIGINT NOT NULL REFERENCES kunden (id) ON DELETE RESTRICT, …
Auf PostgreSQL 17 ausgeführt. Geld als NUMERIC, nicht als float.
SQL PostgreSQL: CREATE TABLE bestellpositionen
CREATE TABLE bestellpositionen ( bestellung_id BIGINT NOT NULL REFERENCES bestellungen (id) ON DELETE CASCADE, position INTEGER NOT NULL, …
Auf PostgreSQL 17 ausgeführt. Geld als NUMERIC, nicht als float.
SQL PostgreSQL: Shop-Schema komplett (4 Tabellen)
CREATE TABLE kunden ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, kundennummer TEXT NOT NULL UNIQUE, …
Auf PostgreSQL 17 ausgeführt. Geld als NUMERIC, nicht als float.
SQL MySQL: CREATE TABLE kunden
CREATE TABLE kunden ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, kundennummer VARCHAR(20) NOT NULL UNIQUE, …
Nach der MySQL-8.0-Referenz geschrieben, nicht ausgeführt. CHECK wird ab 8.0.16 erzwungen, DEFAULT (CURRENT_DATE) ab 8.0.13.
SQL MySQL: CREATE TABLE produkte
CREATE TABLE produkte ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, sku VARCHAR(40) NOT NULL UNIQUE, …
Nach der MySQL-8.0-Referenz geschrieben, nicht ausgeführt. CHECK wird ab 8.0.16 erzwungen, DEFAULT (CURRENT_DATE) ab 8.0.13.
SQL MySQL: CREATE TABLE bestellungen
CREATE TABLE bestellungen ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, kunde_id BIGINT UNSIGNED NOT NULL, …
Nach der MySQL-8.0-Referenz geschrieben, nicht ausgeführt. CHECK wird ab 8.0.16 erzwungen, DEFAULT (CURRENT_DATE) ab 8.0.13.
SQL MySQL: CREATE TABLE bestellpositionen
CREATE TABLE bestellpositionen ( bestellung_id BIGINT UNSIGNED NOT NULL, position INT NOT NULL, …
Nach der MySQL-8.0-Referenz geschrieben, nicht ausgeführt. CHECK wird ab 8.0.16 erzwungen, DEFAULT (CURRENT_DATE) ab 8.0.13.
SQL MySQL: Shop-Schema komplett (4 Tabellen)
CREATE TABLE kunden ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, kundennummer VARCHAR(20) NOT NULL UNIQUE, …
Nach der MySQL-8.0-Referenz geschrieben, nicht ausgeführt. CHECK wird ab 8.0.16 erzwungen, DEFAULT (CURRENT_DATE) ab 8.0.13.
SQL SQLite: CREATE TABLE kunden
CREATE TABLE kunden ( id INTEGER PRIMARY KEY AUTOINCREMENT, kundennummer TEXT NOT NULL UNIQUE, …
Mit SQLite ausgeführt. Fremdschlüssel greifen erst nach PRAGMA foreign_keys = ON. Geld besser als ganze Cent speichern.
SQL SQLite: CREATE TABLE produkte
CREATE TABLE produkte ( id INTEGER PRIMARY KEY AUTOINCREMENT, sku TEXT NOT NULL UNIQUE, …
Mit SQLite ausgeführt. Fremdschlüssel greifen erst nach PRAGMA foreign_keys = ON. Geld besser als ganze Cent speichern.
SQL SQLite: CREATE TABLE bestellungen
CREATE TABLE bestellungen ( id INTEGER PRIMARY KEY AUTOINCREMENT, kunde_id INTEGER NOT NULL REFERENCES kunden (id) ON DELETE RESTRICT, …
Mit SQLite ausgeführt. Fremdschlüssel greifen erst nach PRAGMA foreign_keys = ON. Geld besser als ganze Cent speichern.
SQL SQLite: CREATE TABLE bestellpositionen
CREATE TABLE bestellpositionen ( bestellung_id INTEGER NOT NULL REFERENCES bestellungen (id) ON DELETE CASCADE, position INTEGER NOT NULL, …
Mit SQLite ausgeführt. Fremdschlüssel greifen erst nach PRAGMA foreign_keys = ON. Geld besser als ganze Cent speichern.
SQL SQLite: Shop-Schema komplett (4 Tabellen)
PRAGMA foreign_keys = ON; CREATE TABLE kunden ( …
Mit SQLite ausgeführt. Fremdschlüssel greifen erst nach PRAGMA foreign_keys = ON. Geld besser als ganze Cent speichern.
SQL: INSERT mit drei KundenFiktiv
INSERT INTO kunden (kundennummer, vorname, nachname, email, ort) VALUES ('K-100001', 'Anna', 'Müller', 'anna.mueller@example.com', 'Musterstadt'), …Läuft unverändert in PostgreSQL, MySQL und SQLite.
SQL: INSERT mit vier Produkten (netto, MwSt 19 und 7)Fiktiv
INSERT INTO produkte (sku, name, preis_netto, mwst_satz, bestand) VALUES ('SKU-1001', 'Notizbuch A5', 4.90, 19, 120), …Läuft unverändert in PostgreSQL, MySQL und SQLite. Hier gelten nur Beispielpreise.
SQL: Bestellung mit zwei Positionen anlegenFiktiv
INSERT INTO bestellungen (kunde_id, bestelldatum, status) VALUES (1, '2026-10-09', 'offen'); …
Setzt voraus, dass Kunde 1 und Produkte 1 und 2 existieren und die Bestellung die ID 1 bekommt.
SQL: INSERT mit Apostroph und Umlauten im TextFiktiv
INSERT INTO kunden (kundennummer, vorname, nachname, email, ort) VALUES ('K-100004', 'Conan', 'O''Brien', 'conan.obrien@example.com', 'Düsseldorf-Größenwahn');Ein Apostroph im Text wird im SQL-Standard verdoppelt. In Programmcode besser Parameter verwenden.
SQL PostgreSQL: UPSERT mit ON CONFLICTFiktiv
INSERT INTO kunden (kundennummer, vorname, nachname, email, ort) VALUES ('K-100001', 'Anna', 'Müller', 'anna.mueller@example.com', 'Neustadt') ON CONFLICT (kundennummer) DO UPDATE …Auf PostgreSQL 17 ausgeführt. Braucht einen UNIQUE-Index auf kundennummer. Mit DO NOTHING bleibt die vorhandene Zeile unverändert.
SQL SQLite: UPSERT mit ON CONFLICTFiktiv
INSERT INTO kunden (kundennummer, vorname, nachname, email, ort) VALUES ('K-100001', 'Anna', 'Müller', 'anna.mueller@example.com', 'Neustadt') ON CONFLICT (kundennummer) DO UPDATE …Mit SQLite 3.24 oder neuer ausgeführt.
SQL MySQL: UPSERT mit ON DUPLICATE KEY UPDATEFiktiv
INSERT INTO kunden (kundennummer, vorname, nachname, email, ort) VALUES ('K-100001', 'Anna', 'Müller', 'anna.mueller@example.com', 'Neustadt') AS neu ON DUPLICATE KEY UPDATE …Nach der MySQL-8.0.19-Referenz (Alias statt des veralteten VALUES()), nicht ausgeführt. MariaDB kennt den Alias nicht.
SQL MySQL/MariaDB: UPSERT mit VALUES() (ältere Schreibweise)Fiktiv
INSERT INTO kunden (kundennummer, vorname, nachname, email, ort) VALUES ('K-100001', 'Anna', 'Müller', 'anna.mueller@example.com', 'Neustadt') ON DUPLICATE KEY UPDATE …Nicht ausgeführt. Seit MySQL 8.0.20 als veraltet markiert, in MariaDB weiterhin üblich.
SQL PostgreSQL 15+: MERGEFiktiv
MERGE INTO produkte AS p USING (VALUES ('SKU-1001', 25)) AS neu (sku, zugang) ON p.sku = neu.sku …Auf PostgreSQL 17 ausgeführt; MERGE gibt es ab Version 15.
SQL: Bestand verringern, ohne unter 0 zu fallen
UPDATE produkte SET bestand = bestand - 3 WHERE sku = 'SKU-1001' …
Die Bedingung bestand >= 3 verhindert Überverkauf auch bei gleichzeitigen Zugriffen; prüfen, ob eine Zeile geändert wurde.
SQL: DELETE mit Bedingung und Kontrollabfrage
-- erst zählen SELECT COUNT(*) FROM bestellungen WHERE status = 'storniert' AND bestelldatum < '2025-01-01'; …
SQL: Kunden mit ihren Bestellungen (JOIN)
SELECT k.kundennummer, k.nachname, b.id AS bestellung, b.bestelldatum, b.status FROM kunden AS k JOIN bestellungen AS b ON b.kunde_id = k.id …
SQL: Kunden ohne Bestellung finden (LEFT JOIN)
SELECT k.kundennummer, k.vorname, k.nachname FROM kunden AS k LEFT JOIN bestellungen AS b ON b.kunde_id = k.id …
SQL: Umsatz je Bestellung mit GROUP BY
SELECT bestellung_id, COUNT(*) AS positionen, SUM(menge * einzelpreis) AS summe_netto …SQL: WITH-Abfrage (CTE) für Umsatz pro Kunde
WITH summen AS ( SELECT bestellung_id, SUM(menge * einzelpreis) AS netto FROM bestellpositionen …
SQL: Fensterfunktion ROW_NUMBER je Kunde
SELECT kunde_id, id AS bestellung, bestelldatum, ROW_NUMBER() OVER (PARTITION BY kunde_id ORDER BY bestelldatum DESC) AS nr FROM bestellungen;Fensterfunktionen gibt es in PostgreSQL, MySQL ab 8.0 und SQLite ab 3.25.
SQL: Seitenweise Abfrage mit LIMIT und OFFSET
SELECT id, nachname FROM kunden ORDER BY id …
Seite 3 bei 20 Einträgen pro Seite. Bei großen Tabellen wird hohes OFFSET langsam.
SQL: Seitenweise Abfrage mit Keyset (nach letzter ID)
SELECT id, nachname FROM kunden WHERE id > 40 …
SQL: Transaktion mit Rollback-Möglichkeit
BEGIN; UPDATE produkte SET bestand = bestand - 1 WHERE sku = 'SKU-1001' AND bestand >= 1; …
In MySQL heißt der Beginn START TRANSACTION (BEGIN funktioniert ebenfalls).
SQL: Index, zusammengesetzter Index und eindeutiger Index
CREATE INDEX idx_kunden_nachname ON kunden (nachname); CREATE INDEX idx_kunden_ort_nachname ON kunden (ort, nachname); CREATE UNIQUE INDEX idx_produkte_sku ON produkte (sku);
SQL: Spalte hinzufügen, Standardwert setzen
ALTER TABLE kunden ADD COLUMN telefon VARCHAR(30); ALTER TABLE kunden ADD COLUMN newsletter BOOLEAN NOT NULL DEFAULT FALSE;
VARCHAR/BOOLEAN laufen in PostgreSQL, MySQL und SQLite; SQLite kann mehrere Änderungen nicht in einer ALTER-Anweisung bündeln.
SQL: Soft Delete mit Spalte geloescht_am
ALTER TABLE kunden ADD COLUMN geloescht_am TIMESTAMP NULL; UPDATE kunden SET geloescht_am = CURRENT_TIMESTAMP WHERE kundennummer = 'K-100003'; …
SQL: LIKE mit maskiertem Prozentzeichen
SELECT * FROM produkte WHERE name LIKE '100!%%' ESCAPE '!';
Findet Namen, die mit "100%" beginnen. ESCAPE legt das Maskierungszeichen fest.
SQL: NULL richtig vergleichen
SELECT * FROM kunden WHERE email IS NULL; SELECT * FROM kunden WHERE email IS NOT NULL; SELECT COALESCE(email, 'keine E-Mail') FROM kunden; …
SQL: CASE-Ausdruck für Statustexte
SELECT id, CASE status WHEN 'offen' THEN 'Wartet auf Zahlung' …SQL: Brutto aus Netto berechnen und runden
SELECT sku, name, preis_netto, mwst_satz, ROUND(preis_netto * (1 + mwst_satz / 100), 2) AS preis_brutto FROM produkte;SQL PostgreSQL: Datumsfunktionen
SELECT now(), CURRENT_DATE, date_trunc('month', now()) AS monatsanfang, CURRENT_DATE - INTERVAL '30 days' AS vor_30_tagen, …Auf PostgreSQL 17 ausgeführt.
SQL MySQL: Datumsfunktionen
SELECT NOW(), CURDATE(), DATE_FORMAT(NOW(), '%d.%m.%Y') AS deutsch, DATE_SUB(CURDATE(), INTERVAL 30 DAY) AS vor_30_tagen, …Nach der MySQL-Referenz geschrieben, nicht ausgeführt.
SQL SQLite: Datumsfunktionen
SELECT datetime('now'), date('now'), strftime('%d.%m.%Y', 'now') AS deutsch, date('now', '-30 days') AS vor_30_tagen, …Mit SQLite ausgeführt.
SQL PostgreSQL: JSONB-Spalte abfragen
CREATE TABLE ereignisse (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, daten JSONB NOT NULL); INSERT INTO ereignisse (daten) VALUES ('{"typ": "login", "nutzer": {"id": 7, "name": "Anna"}}'); SELECT daten->>'typ' AS typ, daten->'nutzer …Auf PostgreSQL 17 ausgeführt.
SQL SQLite: JSON-Funktionen
SELECT json_extract('{"typ": "login", "nutzer": {"id": 7}}', '$.nutzer.id') AS id, json_array_length('[1,2,3]') AS anzahl;Mit SQLite ausgeführt (JSON-Funktionen sind in aktuellen Versionen eingebaut).
Ungültiges SQL: Komma vor FROMAbsichtlich ungültig
SELECT id, nachname, FROM kunden;
Ungültiges SQL: Zeichenkette nicht geschlossenAbsichtlich ungültig
SELECT * FROM kunden WHERE nachname = 'O'Brien';
Der Apostroph muss verdoppelt werden (O''Brien) oder als Parameter übergeben werden.
Weitere Themen in Datenformate & Snippets
- JSON Beispiele: gültig und ungültig zum Testen
- XML Beispiele: Namespaces, CDATA und Fehlerfälle
- YAML Beispiele: Listen, Anker und typische Fallen
- Markdown Beispiele: Tabelle, Listen, Codeblock
- HTML Beispiele: Formulare, Tabellen und Meta-Tags
- GraphQL Beispiele: Schema, Query, Mutation, Antwort
- Code-Snippets: Kundenmodell in vielen Sprachen
- curl Beispiele: GET, POST, Header, Upload, Auth