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
Ranglisten in SQL mit Fensterfunktionen – ROW_NUMBER, RANK und PARTITION BY
Für eine Rangliste brauchst du keine Zählerei im Code. ROW_NUMBER() und RANK() vergeben den Platz direkt in SQL – auch getrennt nach Gruppen. Ich zeige dir den Unterschied und wann du was nimmst.
PDO mit SQLite – sichere Abfragen per Prepared Statement
SQLite ist eine ganze Datenbank in einer Datei – perfekt für kleine Projekte. Mit PDO und Prepared Statements fragst du sie sicher ab, ganz ohne SQL-Injection.
Dateien sicher zum Download ausliefern in PHP – mit readfile() und den richtigen Headern
Ein Download-Link auf eine private Datei ist schnell gefährlich – Pfad-Tricks führen sonst zu fremden Dateien. So lieferst du Downloads sauber aus: geschützt vor „../", speicherschonend und mit korrektem Dateinamen.