Menu

SQLite Type Affinity: So tickt das Spaltentyp-System

Wie das Type-Affinity-System von SQLite funktioniert: die fünf Affinitäten, nach welchen Regeln SQLite sie aus deiner Spaltendeklaration ableitet – und warum in einer INTEGER-Spalte plötzlich ein String landen kann.

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

Affinity ist eine Präferenz, keine Regel

SQLite arbeitet mit dynamischer Typisierung. Jeder Wert trägt seine eigene Speicherklasse mit sich (NULL, INTEGER, REAL, TEXT, BLOB), und der deklarierte Spaltentyp schränkt nicht strikt ein, was du dort ablegen darfst. Was der deklarierte Typ wirklich macht: Er gibt der Spalte eine Affinität — also eine bevorzugte Speicherklasse, in die SQLite eingehende Werte umzuwandeln versucht.

Schau dir an, was passiert, wenn die Affinität allein nicht reicht, um einen Typkonflikt zu verhindern:

Die zweite Zeile speichert den String 'two' in einer INTEGER-Spalte. SQLite hat versucht, 'two' in eine Zahl umzuwandeln, hat es nicht geschafft (kein numerischer Wert) – und es trotzdem als TEXT abgelegt. typeof() zeigt dir die tatsächliche Speicherklasse jedes Wertes an, und die deckt sich nicht immer mit dem, was die Spaltendeklaration vermuten lässt.

Wer von Postgres oder MySQL kommt, ist davon erstmal irritiert. Aber genau so ist es in SQLite gewollt.

Die fünf Type Affinities in SQLite

Jede Spalte in einer Tabelle ohne STRICT-Modus bekommt genau eine dieser Affinitäten zugewiesen:

  • TEXT – bevorzugt Strings.
  • NUMERIC – bevorzugt Zahlen, akzeptiert aber auch Text, wenn die Umwandlung nicht klappt.
  • INTEGER – wie NUMERIC, speichert Werte ohne Nachkommastellen aber als Integer.
  • REAL – bevorzugt Fließkommazahlen.
  • BLOB – keine Vorliebe, speichert das, was du übergibst, einfach so.

Die BLOB-Affinität nennt man auch „keine Affinität" – sie greift, wenn du gar keinen Datentyp angibst.

Gleicher Input – der String '42' – fünf unterschiedlich gespeicherte Typen. Jede Spalte hat ihn entsprechend ihrer Affinity konvertiert (oder eben nicht).

Wie SQLite die Affinity aus deiner Deklaration ableitet

Hier kommt der Punkt, an dem viele stolpern: SQLite hat keine feste Liste „gültiger" Datentypen. Du kannst hinter einem Spaltennamen praktisch alles schreiben, und SQLite ermittelt die Affinity, indem es den Text in dieser Reihenfolge nach Teilstrings durchsucht:

  1. Enthält INTINTEGER
  2. Enthält CHAR, CLOB oder TEXTTEXT
  3. Enthält BLOB oder gar keinen Typ → BLOB
  4. Enthält REAL, FLOA oder DOUBREAL
  5. Alles andere → NUMERIC

Das ist der komplette Algorithmus. Und er erklärt einiges an scheinbar merkwürdigem Verhalten:

FLOATING_POINTS wird zu INTEGER, weil die Zeichenfolge INT in POINTS vorkommt. Es gewinnt immer die erste passende Regel von oben nach unten. Genau deshalb kann es passieren, dass du beim simplen Übernehmen von Datentypen aus einer anderen Datenbank plötzlich etwas ganz anderes bekommst, als du erwartet hast.

SQLite Typumwandlung in der Praxis: Konvertierungen beim Insert

Die Type Affinity wird vor allem dann relevant, wenn SQLite entscheidet, ob dein Wert konvertiert oder unverändert gespeichert wird. Die Regeln im Überblick:

  • Affinität TEXT: Zahlen und BLOBs werden zu Text konvertiert.
  • Affinitäten NUMERIC, INTEGER, REAL: Text, der wie eine Zahl aussieht, wird konvertiert; alles andere bleibt Text.
  • Affinität BLOB: Es wird nichts konvertiert.

Zeile für Zeile:

  • '123' in einer NUMERIC-Spalte wird zum Integer 123. Die Umwandlung von Text in eine Zahl hat funktioniert – und zwar verlustfrei.
  • '12.5' wird zum Real-Wert 12.5.
  • 'hello' in NUMERIC bleibt Text – da gibt es schlicht keine Zahl, in die man umwandeln könnte.
  • Die TEXT-Spalte verwandelt Zahlen in ihre String-Darstellung.
  • Die BLOB-Spalte speichert alles exakt so, wie es reinkommt – Typ inklusive.

Der feine Unterschied zwischen INTEGER und REAL

Die INTEGER-Affinität verhält sich praktisch genauso wie NUMERIC, aber mit einem kleinen Kniff: Ein Wert wie 3.0, der keinen echten Nachkommaanteil hat, wird platzsparend als Integer 3 abgelegt.

3.0 landet in beiden Spalten als INTEGER – diese Optimierung greift auch bei NUMERIC. 3.5 behält dagegen seinen Nachkommaanteil und bleibt REAL. Merke: Verlass dich nicht auf typeof(), um herauszufinden, ob eine Spalte als INTEGER oder REAL deklariert wurde. Die Funktion zeigt dir nur, was tatsächlich gespeichert ist – und das kann von Zeile zu Zeile unterschiedlich sein.

Wann SQLite Type Affinity zur Falle wird

Die dynamische Typisierung von SQLite ist praktisch – bis sie es nicht mehr ist. In echtem Code begegnen einem vor allem zwei Stolperfallen:

1. Müll-Daten rutschen durch. Wenn deine Anwendung wegen eines Bugs den Wert 'N/A' an eine INTEGER-Spalte schickt, speichert SQLite ihn anstandslos. Spätere Abfragen, die mit der Spalte rechnen, liefern dann komische Ergebnisse oder NULL. Keine Fehlermeldung, keine Warnung – stille Datenkorruption.

2. Vergleiche verhalten sich seltsam. Beim Sortieren und bei Gleichheitsprüfungen behandelt SQLite Werte unterschiedlicher Storage Classes nicht gleich:

Integers werden numerisch sortiert, danach kommen die Textwerte in lexikografischer Reihenfolge — und zwar nach allen Zahlen. Das Ergebnis ist also 2, 3, 10 (die Integers in numerischer Reihenfolge), gefolgt von '20', '100' (die Strings alphabetisch sortiert). In den meisten Fällen genau nicht das, was man eigentlich will.

Wenn du die Inserts selbst in der Hand hast und sauber validierst, reichen normale Tabellen völlig aus. Falls nicht — oder falls du die Typprüfung lieber der Datenbank überlassen möchtest — gibt es eine bessere Lösung.

Als Nächstes: STRICT Tables

Mit SQLite 3.37 kamen STRICT Tables dazu. Sie schalten die Type Affinity ab und weisen Werte zurück, die nicht zum deklarierten Spaltentyp passen. So bekommst du die dynamische Typisierung von SQLite dort, wo du sie willst, und eine strikte Typprüfung wie in Postgres dort, wo du sie brauchst. Genau darum geht es auf der nächsten Seite.

Häufig gestellte Fragen

Was ist Type Affinity in SQLite überhaupt?

Type Affinity ist die bevorzugte Speicherklasse einer Spalte. SQLite kennt davon fünf: TEXT, NUMERIC, INTEGER, REAL und BLOB. Beim Insert versucht SQLite, den Wert in die Affinität der Spalte zu konvertieren – wäre die Konvertierung verlustbehaftet oder gar nicht möglich, wird der Wert einfach so gespeichert, wie er reinkommt. Affinity ist also eher ein Hinweis als eine harte Regel.

Wie bestimmt SQLite die Affinität einer Spalte?

SQLite durchsucht den Typnamen aus deinem CREATE TABLE der Reihe nach nach Teilstrings: enthält er INT, wird's INTEGER; sonst machen CHAR, CLOB oder TEXT daraus TEXT; sonst ergibt BLOB (oder gar kein Typ) ein BLOB; sonst führen REAL, FLOA oder DOUB zu REAL; alles andere wird NUMERIC. Genau deshalb landet VARCHAR(50) bei TEXT und BIGINT bei INTEGER – die Wörter, die du hinschreibst, werden schlicht per Pattern-Matching ausgewertet.

Kann eine SQLite-Spalte Werte vom „falschen“ Typ enthalten?

In normalen Tabellen: ja. Eine als INTEGER deklarierte Spalte speichert klaglos den String 'hello', weil Affinity nur eine Konvertierung vorschlägt. Wenn du echte Typprüfung willst, nimm STRICT-Tabellen – die weisen unpassende Werte direkt ab. Darum geht's im nächsten Abschnitt.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S