CREATE TABLE definiert ein Schema
Strukturierte Daten leben in SQLite immer in Tabellen – und jede Tabelle beginnt mit einem CREATE TABLE-Statement. Du vergibst einen Namen, listest die Spalten auf und kannst optional noch Constraints dranhängen. SQLite schreibt das Schema in die Datenbankdatei, und schon ist die Tabelle einsatzbereit.
Das kleinste sinnvolle Beispiel sieht so aus:
Drei Spalten, ein Primärschlüssel, eine NOT NULL-Regel. SQLite hat die id automatisch vergeben, weil es sich um einen Integer-Primärschlüssel handelt, und email durfte in der zweiten Zeile NULL sein, weil nichts anderes festgelegt war. Mehr ist es eigentlich nicht — Name, Spalten, Constraints — und alles Weitere auf dieser Seite ist nur eine Variation davon.
Die Syntax im Detail
Eine Spaltendefinition folgt dem Muster name TYP constraint constraint .... Der Typ ist im klassischen SQLite optional (dazu gleich mehr auf der Seite zur Type Affinity), aber es lohnt sich, ihn immer anzugeben — sowohl andere Entwickler als auch Tools verlassen sich darauf.
Ein paar Dinge, die hier wichtig sind:
- Constraints werden einfach mit Leerzeichen aneinandergereiht:
NOT NULL UNIQUEaufskuheißt, dass beide Regeln gelten. DEFAULT 1beiin_stocksorgt dafür, dass derINSERTdiese Spalte weglassen darf.- SQLite verwendet
INTEGERfür boolesche Werte – einen echtenBOOLEAN-Typ gibt es nicht.0steht für falsch,1für wahr. - Ein Komma nach der letzten Spalte ist ein Syntaxfehler. SQL ist hier strenger als JavaScript.
SQLite CREATE TABLE IF NOT EXISTS: kein Crash beim erneuten Ausführen
Führst du ein CREATE TABLE gegen eine Datenbank aus, in der die Tabelle schon existiert, wirft SQLite einen Fehler:
Fehler: Tabelle users existiert bereits
Beim ersten Mal ist das okay, beim hundertsten Mal nur noch nervig. Mit IF NOT EXISTS wird das Statement zu einer No-Op, sobald die Tabelle bereits existiert:
Das zweite CREATE TABLE macht schlicht gar nichts — kein Fehler, keine Schemaänderung. Genau diese Variante willst du in Startup-Code, Migrationsskripten und überall dort haben, wo dasselbe SQL mehrfach laufen kann.
Ein Wort der Warnung: IF NOT EXISTS prüft nur den Namen. Wenn bereits eine Tabelle mit diesem Namen existiert, aber andere Spalten hat, lässt SQLite sie unangetastet. Das Schema wird nicht "repariert" oder "aktualisiert" — dafür gibt es Migrationen.
Constraints: Regeln, die fest am Schema kleben
Mit Constraints verlagerst du Validierung direkt in die Datenbank. Diese vier wirst du ständig brauchen:
PRIMARY KEY— identifiziert eine Zeile eindeutig. Mehr dazu findest du im Doc zu Primary Keys.NOT NULL— die Spalte muss einen Wert enthalten.DEFAULT wert— wird verwendet, wenn einINSERTdie Spalte auslässt. Erlaubt sind sowohl Literale als auch Ausdrücke wiedatetime('now').CHECK (ausdruck)— muss für jede Zeiletrueergeben.UNIQUE (spalte, spalte)— Constraint auf Tabellenebene, der die Kombination eindeutig hält.
Constraints werden bei jedem INSERT und UPDATE geprüft. Verletzt eine Zeile eine Bedingung, wird sie abgewiesen und das Statement schlägt fehl. Fehlerhafte Daten direkt in der Datenbank abzufangen ist deutlich günstiger, als sie später aus der Applikation wieder herauszufischen.
Foreign Keys in SQLite
Ein Foreign Key sagt im Grunde: „Diese Spalte verweist auf eine Zeile in einer anderen Tabelle." Damit bleiben deine Daten konsistent — du kannst keinen Nutzer referenzieren, der gar nicht existiert, und mit den passenden Optionen kann das Löschen eines Nutzers automatisch dessen Bestellungen mitnehmen (Cascade).
Eine SQLite-Falle, die man sich gut einprägen sollte: Die Foreign-Key-Prüfung ist standardmäßig deaktiviert. Für jede Verbindung, die diese Constraints durchsetzen soll, muss PRAGMA foreign_keys = ON ausgeführt werden. Die meisten Treiber erledigen das automatisch oder bieten dafür eine Einstellung an – falls deiner das nicht tut, setz das Pragma direkt nach dem Verbindungsaufbau ab.
ON DELETE CASCADE bedeutet hier: Wird ein User gelöscht, verschwinden auch seine Posts mit. Daneben gibt es noch SET NULL, RESTRICT sowie das Default-Verhalten NO ACTION, das den Löschvorgang verweigert, sobald noch abhängige Datensätze existieren.
SQLite CREATE TABLE AS SELECT: Tabelle aus Abfrage erzeugen
Manchmal brauchst du einfach eine schnelle Kopie eines Abfrageergebnisses als neue Tabelle – als Snapshot, Backup oder Spieltabelle für eine Analyse. Genau dafür gibt es CREATE TABLE ... AS SELECT:
Die neue Tabelle übernimmt die Spaltennamen, die Typen (so gut es geht) und die Daten. Mindestens genauso wichtig ist aber, was eben nicht mitkommt: kein Primary Key, kein NOT NULL, keine Indizes, keine Foreign Keys. Du bekommst nur einen flachen Snapshot. Nutze das also als Ausgangspunkt für schnelle Auswertungen – und nicht, um ein echtes Schema zu klonen.
Wenn du nur die Struktur ohne die Daten willst, hängst du einfach WHERE 0 an:
Damit bekommst du eine leere Tabelle mit identischer Spaltenstruktur – praktisch für Archivtabellen, die du später befüllst.
Temporäre Tabellen in SQLite
Eine TEMP-Tabelle existiert nur, solange die aktuelle Datenbankverbindung offen ist. Sobald du die Verbindung schließt, ist sie weg – kein Aufräumen nötig, kein Schema-Müll, der zurückbleibt:
Sinnvolle Einsatzgebiete: Zeilen für eine mehrstufige Abfrage zwischenspeichern, Zwischenergebnisse parken, die für einen CTE zu unhandlich sind, oder verbindungsspezifische Daten in einer länger laufenden Session abschotten. CREATE TEMP TABLE und CREATE TEMPORARY TABLE bedeuten dasselbe.
Du kannst das auch mit AS SELECT kombinieren: CREATE TEMP TABLE snapshot AS SELECT ... ist ein typisches Muster, um ein Zwischenergebnis mitten in einer Analyse einzufrieren.
Namen in Anführungszeichen setzen
Spalten- und Tabellennamen sind in den meisten Fällen schlichte Bezeichner. Falls du aber ein reserviertes Wort oder einen Namen mit Leerzeichen brauchst, packst du ihn entweder in doppelte Anführungszeichen (so will es der SQL-Standard) oder in Backticks (eine MySQL-Eigenheit, die SQLite ebenfalls akzeptiert):
Es funktioniert, aber jedes Mal, wenn du die Tabelle ansprichst, hast du diese kleine Reibung. Bleib lieber bei schlichten Namen wie orders, selection oder user_id und spar dir die Anführungszeichen komplett.
Ein praxisnahes Beispiel
Jetzt setzen wir alles zusammen — ein kleines Schema für eine Tasks-App, mit IF NOT EXISTS, damit es bei jedem Start problemlos durchläuft:
Damit hast du ein Schema, das produktionsreif ist: idempotentes Anlegen, aktivierte Foreign Keys, ein CHECK, das done ehrlich hält, sinnvolle Defaults und Zeitstempel, die sich von selbst füllen.
Weiter geht's: Datentypen
Bei CREATE TABLE kannst du INTEGER, TEXT oder REAL schreiben – nur nimmt SQLite das mit den Typen bekanntermaßen nicht so genau. Auf der nächsten Seite schauen wir uns die fünf Storage Classes an, die SQLite tatsächlich intern verwendet – und warum der Typ, den du hingeschrieben hast, nicht immer der Typ ist, den du am Ende bekommst.
Häufig gestellte Fragen
Wie lege ich in SQLite eine Tabelle an?
Mit CREATE TABLE name (spalte1 TYP, spalte2 TYP, ...). Jede Spalte bekommt einen Namen und optional einen Datentyp; Constraints wie PRIMARY KEY, NOT NULL oder DEFAULT lassen sich direkt mit angeben. Das Statement wird sofort ausgeführt, und die Tabelle bleibt dauerhaft in der Datenbankdatei erhalten.
Was bewirkt IF NOT EXISTS bei CREATE TABLE?
CREATE TABLE IF NOT EXISTS name (...) legt die Tabelle nur an, wenn noch keine mit diesem Namen existiert. Ohne diesen Zusatz fliegt dir beim erneuten Ausführen gegen eine bestehende Datenbank ein table already exists-Fehler um die Ohren. In Migrations-Skripten und beim App-Start gehört das praktisch immer dazu.
Kann ich in SQLite eine Tabelle aus einem SELECT erzeugen?
Ja – CREATE TABLE new_name AS SELECT ... baut aus dem Ergebnis einer Abfrage eine neue Tabelle. Spaltennamen und Daten werden übernommen, Constraints, Primary Keys und Indizes der Quelltabelle dagegen nicht. Gut für Snapshots oder Zwischentabellen, aber kein Ersatz für ein sauber definiertes Schema.
Was ist der Unterschied zwischen einer temporären und einer normalen Tabelle?
CREATE TEMP TABLE (bzw. CREATE TEMPORARY TABLE) erzeugt eine Tabelle, die nur für die aktuelle Verbindung existiert und beim Schließen verschwindet. Normale Tabellen werden dauerhaft in der Datenbankdatei gespeichert. Temp-Tabellen eignen sich super, um Zwischenergebnisse zu parken, ohne das eigentliche Schema vollzumüllen.