Einfügen oder aktualisieren – in einer Anweisung
SELECT prüfen und danach INSERT oder UPDATE schicken ist umständlich – und bei gleichzeitigen Zugriffen falsch.UPDATE aus. Die abgelehnte neue Zeile heißt excluded – so übernimmst du gezielt einzelne Spalten und lässt andere (hier bestand) in Ruhe.🧪 Ausprobieren – Vorher / Nachher
🌍 Dialekte nebeneinander
Ausgeführt werden nur SQLite (sql.js) und – wo markiert – PostgreSQL (PGlite). MySQL/MariaDB, SQL Server und Oracle sind nach der offiziellen Dokumentation geschrieben, laufen hier aber nicht.
INSERT INTO artikel (artikel_nr, bezeichnung, kategorie, preis, bestand) VALUES ('A-100', 'Teekanne Glas', 'Küche', 26.50, 0) ON CONFLICT (artikel_nr) DO UPDATE SET preis = EXCLUDED.preis;
Ab PostgreSQL 15 alternativ MERGE. Der Trick xmax <> 0 in RETURNING (zeigt Update statt Insert) ist ein Implementierungsdetail, keine dokumentierte Schnittstelle.
INSERT INTO artikel (artikel_nr, bezeichnung, kategorie, preis, bestand) VALUES ('A-100', 'Teekanne Glas', 'Küche', 26.50, 0) AS neu ON DUPLICATE KEY UPDATE preis = neu.preis; -- MariaDB und MySQL < 8.0.19: -- … ON DUPLICATE KEY UPDATE preis = VALUES(preis); -- VALUES() ist in MySQL seit 8.0.20 als veraltet markiert.
Rückgabe „affected rows“: 1 = eingefügt, 2 = aktualisiert, 0 = unverändert. Greift bei jedem PRIMARY KEY oder UNIQUE-Index – ein Konfliktziel wie ON CONFLICT (…) gibt es nicht.
MERGE INTO artikel WITH (HOLDLOCK) AS z USING (VALUES ('A-100', 'Teekanne Glas', 'Küche', 26.50, 0)) AS q (artikel_nr, bezeichnung, kategorie, preis, bestand) ON z.artikel_nr = q.artikel_nr WHEN MATCHED THEN UPDATE SET preis = q.preis WHEN NOT MATCHED THEN INSERT (artikel_nr, bezeichnung, kategorie, preis, bestand) VALUES (q.artikel_nr, q.bezeichnung, q.kategorie, q.preis, q.bestand);
MERGE muss mit Semikolon enden. HOLDLOCK (= SERIALIZABLE) verhindert, dass zwei Sitzungen gleichzeitig „NOT MATCHED“ sehen.
MERGE INTO artikel z USING (SELECT 'A-100' AS artikel_nr, 'Teekanne Glas' AS bezeichnung, 'Küche' AS kategorie, 26.50 AS preis, 0 AS bestand FROM dual) q ON (z.artikel_nr = q.artikel_nr) WHEN MATCHED THEN UPDATE SET z.preis = q.preis WHEN NOT MATCHED THEN INSERT (artikel_nr, bezeichnung, kategorie, preis, bestand) VALUES (q.artikel_nr, q.bezeichnung, q.kategorie, q.preis, q.bestand);
Die ON-Bedingung steht in Klammern; Spalten aus der ON-Bedingung dürfen im UPDATE nicht geändert werden.
INSERT INTO artikel (artikel_nr, bezeichnung, kategorie, preis, bestand) VALUES ('A-100', 'Teekanne Glas', 'Küche', 26.50, 0) ON CONFLICT (artikel_nr) DO UPDATE SET preis = excluded.preis;
Syntax nach dem Vorbild von PostgreSQL. Mehrere ON CONFLICT-Klauseln sind ab 3.35.0 erlaubt.
⚠️ Fallstricke
- Das Konfliktziel
ON CONFLICT (spalten)braucht einen passenden PRIMARY KEY oder UNIQUE-Index – sonst Fehler. excludedenthält die vorgeschlagene Zeile inklusive Default-Werten.SET bestand = excluded.bestandwürde den Lagerbestand hier auf 0 setzen.- Unnötige Schreibvorgänge vermeiden:
DO UPDATE SET … WHERE artikel.preis <> excluded.preis– dann feuern UPDATE-Trigger nur bei echten Änderungen. - PostgreSQL erlaubt nicht, dass eine Anweisung dieselbe Zeile zweimal trifft (zweimal A-100 in VALUES) – laut Doku ein Cardinality-Violation-Fehler („… cannot affect row a second time“). Quelldaten vorher entdoppeln.
📚 Belege
- PostgreSQL: INSERT – ON CONFLICT – ON CONFLICT DO UPDATE/NOTHING, EXCLUDED, atomares Insert-oder-Update, keine Zeile zweimal pro Befehl
- PostgreSQL: Release Notes 9.5 – INSERT … ON CONFLICT seit 9.5
- SQLite: UPSERT – UPSERT seit 3.24.0, mehrere ON CONFLICT-Klauseln seit 3.35.0, „WHERE true“ bei INSERT … SELECT
- MySQL: INSERT … ON DUPLICATE KEY UPDATE – Zeilen-Alias seit 8.0.19, VALUES() deprecated seit 8.0.20, affected rows 1/2/0
- MariaDB: INSERT ON DUPLICATE KEY UPDATE – VALUES() in ON DUPLICATE KEY UPDATE (nicht veraltet, kein Zeilen-Alias)
- SQL Server: MERGE (Transact-SQL) – MERGE-Syntax, OUTPUT $action, HOLDLOCK gegen Unique-Verletzungen
- SQL Server: MERGE in SQL Server 2008 – MERGE seit SQL Server 2008
- Oracle: MERGE – MERGE-Syntax (ON in Klammern, DELETE WHERE)
- Oracle: Neue Features 9i Release 1 – MERGE und Flashback Query seit 9i