Menu

SQLite STRICT Tables: Echte Typprüfung erzwingen

Wie du mit STRICT Tables in SQLite die flexible Typablage abschaltest, falsche Werte abweisen lässt und endlich die Typprüfung bekommst, die du eigentlich erwartet hast.

Diese Seite enthält ausführbare Editoren — bearbeiten, ausführen und Ausgabe sofort sehen.

Wozu gibt es STRICT Tables?

Der Umgang von SQLite mit Datentypen ist bekanntermaßen ziemlich locker. Du deklarierst eine Spalte als INTEGER, schiebst den String "hello" rein – und SQLite zuckt mit den Schultern und speichert den String trotzdem. Diese Flexibilität war in den 90ern eine bewusste Design-Entscheidung, sorgt aber bei allen, die von Postgres oder MySQL kommen, regelmäßig für Verwunderung. Und sie versteckt Bugs.

Genau hier kommen STRICT Tables ins Spiel, eingeführt mit SQLite 3.37. Du aktivierst sie pro Tabelle, und ab diesem Moment gelten die deklarierten Spaltentypen wirklich so, wie sie dastehen.

Das Schlüsselwort STRICT steht hinter der schließenden Klammer. Sonst sieht alles aus wie bei einem ganz normalen CREATE TABLE. Der Unterschied wird erst sichtbar, sobald du versuchst, einen Wert vom falschen Typ in eine Spalte zu schreiben.

Was STRICT-Tabellen wirklich erzwingen

Bei einer normalen Tabelle greift die sogenannte Type Affinity: SQLite versucht, den Wert in den deklarierten Typ umzuwandeln, und speichert ihn notfalls einfach so, wie er reinkam. In einer STRICT-Tabelle dagegen führt jede Typabweichung direkt zu einem Fehler.

Versuch das Ganze mal mit einer normalen (Nicht-STRICT) Tabelle — und siehe da: das dritte INSERT läuft anstandslos durch. SQLite legt fröhlich den String 'oops' in einer Spalte ab, die du eigentlich als INTEGER deklariert hast. Monate später liefert dann irgendeine Aggregat-Abfrage Müll zurück, und du verbringst einen halben Nachmittag mit der Fehlersuche. Mit STRICT knallt es genau dort, wo der Fehler entsteht — beim Einfügen — und du kannst ihn sofort beheben.

Die Fehlermeldung sieht so aus:

Laufzeitfehler: TEXT-Wert kann nicht in INTEGER-Spalte accounts.balance gespeichert werden

Klar, sofort sichtbar, kaum zu übersehen.

Die fünf erlaubten Datentypen

In STRICT Tables sind nur fünf Typnamen zulässig:

  • INTEGER — ganze Zahlen.
  • REAL — Fließkommazahlen.
  • TEXT — Zeichenketten.
  • BLOB — Rohbytes.
  • ANY — beliebiger Typ, ohne Konvertierung.

Mehr nicht. Die üblichen Aliase, die SQLite sonst akzeptiert — VARCHAR(255), DOUBLE, BOOLEAN, DATETIME, INT — führen in einer STRICT Table allesamt zu einem Fehler:

Die Fehlermeldung:

Parse-Fehler: Unbekannter Datentyp für bad.name: "VARCHAR(255)"

Die Lösung: Verwende einen der fünf erlaubten Typnamen. Aus VARCHAR(255) wird TEXT, aus DATETIME ebenfalls TEXT (SQLite speichert Datumsangaben sowieso als ISO-Strings) und aus BOOLEAN wird INTEGER (mit 0 und 1).

Die Hintertür mit ANY

ANY ist der einzige Typ, mit dem eine STRICT-Spalte gemischte Werte aufnehmen darf – praktisch zum Beispiel für eine generische value-Spalte in einer Key-Value-Tabelle:

ANY verhält sich in STRICT-Tabellen besonders: Werte werden ohne die sonst übliche Typumwandlung gespeichert. Ein String '100' bleibt ein String, eine Ganzzahl 100 bleibt eine Ganzzahl. Die typeof()-Aufrufe in der Abfrage oben belegen das.

In einer normalen (nicht-STRICT) Tabelle würde eine Spalte mit ANY-Affinität numerisch aussehende Strings automatisch in Zahlen umwandeln. STRICT bewahrt dagegen den ursprünglichen Datentyp exakt.

STRICT und PRIMARY KEY

Ein feiner, aber wichtiger Unterschied: In einer regulären Tabelle ist INTEGER PRIMARY KEY ein Sonderfall – die Spalte wird zum Alias für rowid und akzeptiert ausschließlich Ganzzahlen. Andere Primärschlüssel-Deklarationen sind dagegen deutlich nachsichtiger.

In einer STRICT-Tabelle wird der Spaltentyp dagegen konsequent erzwungen, ganz egal, ob es sich um den Primärschlüssel handelt oder nicht:

Der zweite Insert schlägt fehl. In einer normalen Tabelle (ohne STRICT) würde die 42 klammheimlich in der TEXT-Primärschlüsselspalte landen. Hier bekommst du sofort Bescheid.

STRICT- und normale Tabellen kombinieren

STRICT gilt pro Tabelle, nicht für die ganze Datenbank. Du kannst also eine strikte users-Tabelle und eine lockere events-Tabelle problemlos in derselben Datei haben. Foreign Keys funktionieren zwischen beiden ganz normal weiter.

Die Tabelle events hat weder STRICT noch einen deklarierten Typ für payload – sie schluckt also alles, was man hineinwirft. Gelegentlich praktisch, als Default aber riskant. Untypisierte Spalten solltest du dir für Fälle aufheben, in denen du wirklich eine Sammelspalte für Beliebiges brauchst.

Wann sich STRICT Tables lohnen

Bei neuen Schemas lautet die Antwort: „fast immer". Der Aufwand ist minimal – ein Schlüsselwort pro Tabelle und die fünf zulässigen Typnamen im Kopf behalten. Der Gewinn: Fehler, die sich sonst still in deinen Daten einnisten würden, fliegen direkt beim verursachenden INSERT auf.

Auf STRICT verzichten solltest du, wenn:

  • Du eine alte SQLite-Datenbank pflegst, deren Schema bewusst auf der lockeren Typprüfung aufbaut.
  • Du SQLite-Versionen vor 3.37 (Oktober 2021) unterstützen musst – dort gibt es das Keyword schlicht nicht.
  • Du tatsächlich willst, dass eine Spalte gemischte Typen aufnimmt. Selbst dann ist eine STRICT-Tabelle mit einer ANY-Spalte besser als eine komplett ungetypte Tabelle, weil alle anderen Spalten weiterhin geprüft werden.

Kurze Checkliste, um eine normale Tabelle auf STRICT umzustellen:

  • VARCHAR, CHAR, NVARCHAR durch TEXT ersetzen.
  • DOUBLE, FLOAT, NUMERIC durch REAL ersetzen.
  • BOOLEAN, BIT, TINYINT durch INTEGER ersetzen.
  • DATETIME, TIMESTAMP, DATE durch TEXT ersetzen (oder INTEGER, falls du Unix-Timestamps speicherst).
  • Nach der schließenden Klammer ein STRICT anhängen.

Als Nächstes: Primary Keys

STRICT Tables sorgen dafür, dass Spalten ihre Daten sauber speichern. Der nächste Hebel ist die Frage, welche Spalte eine Zeile eindeutig identifiziert – und SQLites Primary Keys haben ein paar Eigenheiten (vor allem rund um INTEGER PRIMARY KEY und rowid), die du kennen solltest, bevor du ein echtes Schema entwirfst.

Häufig gestellte Fragen

Was ist eine STRICT Table in SQLite?

Eine STRICT Table erzwingt den deklarierten Spaltentyp – sagst du also, eine Spalte ist INTEGER, weist SQLite jeden Wert ab, der weder ein Integer noch NULL ist. Aktiviert wird das Ganze, indem du nach der schließenden Klammer von CREATE TABLE das Schlüsselwort STRICT anhängst. Ohne dieses Keyword greift die übliche Type Affinity: SQLite konvertiert Werte, wenn es geht, und speichert sie ansonsten einfach so, wie sie kommen.

Welche Datentypen sind in einer STRICT Table erlaubt?

Genau fünf: INTEGER, REAL, TEXT, BLOB und ANY. Aliase wie VARCHAR, DOUBLE, BOOLEAN oder DATETIME, die in normalen Tabellen problemlos durchgehen, lösen in einer STRICT Table einen Fehler aus. Der Spaltentyp ANY ist die Notausfahrt – er nimmt jeden Wert ohne Konvertierung entgegen.

Sollte ich für neue SQLite-Datenbanken STRICT Tables verwenden?

Bei den meisten neuen Schemas: ja. STRICT Tables fangen Bugs ab, die normale Tabellen still und heimlich schlucken – einen versehentlichen String in einer INTEGER-Spalte zum Beispiel, oder eine Liste, die irgendwie als REAL serialisiert wurde. Der Preis ist überschaubar: ein zusätzliches Schlüsselwort pro Tabelle und der Verzicht auf exotische Typnamen. Verfügbar ab SQLite 3.37 (2021).

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S