Upsert in SQLite mit ON CONFLICT DO UPDATE
Einfügen oder aktualisieren in einem einzigen Statement – mit ON CONFLICT ... DO UPDATE. Kein vorheriges SELECT, kein Race Condition zwischen Prüfen und Schreiben.
„Wenn der Eintrag existiert, aktualisiere ihn, sonst füge ihn neu ein." Das nennt man Upsert. Statt erst mit einem SELECT zu prüfen und dann zu entscheiden, macht SQLite das atomar in einem Statement – mit ON CONFLICT ... DO UPDATE. Das spart nicht nur Code, sondern schließt auch die Lücke zwischen Prüfen und Schreiben, in der sonst zwei parallele Zugriffe kollidieren könnten.
Voraussetzung: ein Unique-Constraint
Ein Upsert braucht eine Spalte, an der SQLite den Konflikt erkennt – einen PRIMARY KEY oder UNIQUE-Index.
CREATE TABLE seiten_aufrufe (
pfad TEXT PRIMARY KEY,
treffer INTEGER NOT NULL DEFAULT 0,
zuletzt INTEGER NOT NULL
);Das Upsert
INSERT INTO seiten_aufrufe (pfad, treffer, zuletzt)
VALUES ('/start', 1, unixepoch())
ON CONFLICT(pfad) DO UPDATE SET
treffer = treffer + 1,
zuletzt = excluded.zuletzt;Der Clou ist das Schlüsselwort excluded: Es steht für die Zeile, die eingefügt werden sollte. So greifst du im UPDATE-Teil auf die neuen Werte zu – hier setze ich zuletzt auf den gerade übergebenen Zeitstempel, während ich treffer um eins hochzähle.
Nur bei Bedarf aktualisieren
Du kannst den Update-Teil zusätzlich an eine Bedingung knüpfen – etwa nur schreiben, wenn sich wirklich etwas ändert:
INSERT INTO seiten_aufrufe (pfad, treffer, zuletzt)
VALUES ('/start', 1, unixepoch())
ON CONFLICT(pfad) DO UPDATE SET
treffer = treffer + 1,
zuletzt = excluded.zuletzt
WHERE excluded.zuletzt > seiten_aufrufe.zuletzt;Genau dieses Muster nutze ich für die cookielose Aufruf-Statistik in meinem eigenen CMS. Wenn du eine Website mit sauberer, datensparsamer Auswertung brauchst, findest du mich auf bymw.de.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Wie viel mehr als gestern? Differenz zur Vorzeile mit LAG() in SQL
„Wie viele Besucher heute im Vergleich zu gestern?" – der Reflex ist ein Self-Join auf die Vorzeile. Mit der Fensterfunktion LAG() bekommst du den Wert der vorherigen Zeile direkt daneben, ohne die Tabelle ein zweites Mal anzufassen.
RETURNING in SQLite – die geschriebene Zeile sofort zurückbekommen
Nach einem INSERT oder UPDATE noch einmal SELECTen, um zu sehen, was drinsteht? Muss nicht sein. RETURNING liefert die betroffenen Zeilen direkt aus dem schreibenden Statement – ohne zweite Abfrage und ohne die Lücke dazwischen.
WITH RECURSIVE – Kategoriebäume und Kommentar-Threads in einer einzigen Abfrage
Kategorien mit Unterkategorien, Kommentare mit Antworten, Ordner in Ordnern: Die meisten holen sich so etwas mit einer Schleife und einer Abfrage pro Ebene. Eine rekursive CTE holt den ganzen Baum in einem Rutsch – inklusive Tiefe und Pfad.