Menu

SQLite Primary Key: INTEGER, AUTOINCREMENT & Composite

Wie Primärschlüssel in SQLite wirklich funktionieren – vom Sonderfall INTEGER PRIMARY KEY über zusammengesetzte Schlüssel bis AUTOINCREMENT und den typischen Stolperfallen.

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

Was ein Primärschlüssel wirklich macht

Ein Primärschlüssel ist die Spalte (oder die Kombination aus mehreren Spalten), die jede Zeile einer Tabelle eindeutig identifiziert. Zwei Zeilen dürfen niemals denselben Wert im Primärschlüssel haben. SQLite achtet automatisch darauf und nutzt diesen Schlüssel zugleich, um Zeilen blitzschnell zu finden.

Die einfachste Variante schreibst du direkt an die Spalte:

Du hast keine id angegeben, und SQLite hat trotzdem eine eingetragen. Das ist keine Magie, sondern ein Spezialfall von INTEGER PRIMARY KEY, den man verstehen sollte, bevor man weiterschreibt.

Warum INTEGER PRIMARY KEY in SQLite besonders ist

In den meisten Datenbanken ist ein Primärschlüssel einfach nur ein eindeutiger Index. In SQLite besitzt dagegen jede normale Tabelle bereits eine versteckte 64-Bit-Ganzzahl namens rowid, über die Zeilen intern identifiziert werden. Sobald du eine Spalte exakt als INTEGER PRIMARY KEY deklarierst, wird diese Spalte zur rowid. Kein zusätzlicher Index, kein zusätzlicher Speicherplatz — deine ID und die physische Position der Zeile sind ein und dasselbe.

id und rowid sind dieselbe Spalte unter zwei Namen. Eine Suche per id führt direkt zur Zeile – es muss kein zweiter Baum durchlaufen werden. Daher lautet die Standardempfehlung für SQLite: Wer einen numerischen Primärschlüssel will, schreibt exakt INTEGER PRIMARY KEY. Nicht INT, nicht BIGINT, und auch nicht INTEGER NOT NULL PRIMARY KEY (das funktioniert zwar, aber der Typ muss zwingend INTEGER sein).

Andere Typen funktionieren ebenfalls – sie bekommen lediglich einen separaten Unique-Index spendiert. Das ist völlig in Ordnung, nur eben nicht ganz so kompakt.

AUTOINCREMENT ist meistens überflüssig

Wer von anderen Datenbanken kommt, schreibt aus Gewohnheit gerne id INTEGER PRIMARY KEY AUTOINCREMENT. In SQLite macht das Schlüsselwort AUTOINCREMENT allerdings deutlich weniger, als der Name vermuten lässt – und in den meisten Fällen brauchst du es schlicht nicht.

Ohne AUTOINCREMENT füllt sich eine Spalte vom Typ INTEGER PRIMARY KEY automatisch mit dem um eins erhöhten Wert der größten vorhandenen rowid. Löschst du die letzte Zeile, kann deren id beim nächsten Insert wiederverwendet werden.

Mit AUTOINCREMENT merkt sich SQLite die jemals höchste vergebene id in einer Hilfstabelle namens sqlite_sequence und vergibt diesen Wert nie wieder – auch dann nicht, wenn die zugehörige Zeile längst gelöscht wurde.

Die Tabelle plain hat die ID 3 wiederverwendet. Die Tabelle mit AUTOINCREMENT ist dagegen auf 4 gesprungen. Wenn du keinen triftigen Grund hast, eine erneute ID-Vergabe zu unterbinden – etwa für Audits oder externe Referenzen, die nach dem Löschen weiterleben – lass die Finger von AUTOINCREMENT. Es kostet pro Insert einen zusätzlichen Schreibvorgang und braucht eine eigene Verwaltungstabelle.

Zusammengesetzter Primärschlüssel in SQLite

Manchmal reicht eine einzelne Spalte einfach nicht aus. Eine Verknüpfungstabelle, die zum Beispiel Benutzer mit Rollen verbindet, lässt sich erst durch das Paar (user_id, role_id) eindeutig identifizieren. Für solche Fälle deklarierst du den Schlüssel auf Tabellenebene:

Das Paar muss in der gesamten Tabelle eindeutig sein – (1, 10) darf also nur einmal vorkommen. Jede Spalte für sich darf beliebig oft auftauchen. Genau das ist der Sinn der Sache: Ein Nutzer kann mehrere Rollen haben, eine Rolle kann mehreren Nutzern zugewiesen sein, aber jede konkrete Nutzer-Rollen-Kombination existiert höchstens einmal.

Ein zusammengesetzter Primärschlüssel (sqlite composite primary key) legt einen separaten Index über die angegebenen Spalten an. Er wird nicht zur rowid – diese Sonderbehandlung bekommt ausschließlich ein einzelnes INTEGER PRIMARY KEY.

Die NULL-Falle beim Primärschlüssel

Jetzt kommt eine Eigenheit, die alle überrascht, die von PostgreSQL oder MySQL kommen: In einer normalen SQLite-Tabelle darf eine Primärschlüsselspalte, die kein INTEGER PRIMARY KEY ist, tatsächlich NULL-Werte enthalten. Das ist ein altbekannter Bug, den die SQLite-Entwickler aus Gründen der Abwärtskompatibilität nie behoben haben.

Zwei Zeilen mit NULL sind also am Primärschlüssel vorbeigerutscht. Die Lösung: Setze NOT NULL explizit auf jede Primärschlüssel-Spalte, die kein Integer ist:

Oder du nutzt eine STRICT-Tabelle, in der der NULL-im-PK-Bug behoben ist. Es schadet nie, sich anzugewöhnen, jede Primärschlüsselspalte zusätzlich mit NOT NULL zu versehen – günstige Versicherung.

SQLite Primary Key vs. UNIQUE

Beide verhindern Duplikate. Die Unterschiede im Überblick:

  • Eine Tabelle hat höchstens einen Primärschlüssel, aber beliebig viele UNIQUE-Constraints.
  • Der Primärschlüssel ist der „Haupt"-Identifikator der Tabelle – Fremdschlüssel verweisen standardmäßig auf ihn.
  • Ein INTEGER PRIMARY KEY wird zur rowid; eine Integer-Spalte mit UNIQUE dagegen nicht.
  • UNIQUE-Spalten akzeptieren problemlos mehrere NULL-Werte (jedes NULL gilt als eigener Wert).

id ist die Identität der Zeile. email und username sind zwar ebenfalls eindeutig, aber Geschäftsdaten – sie können sich ändern, während die id stabil bleiben sollte.

Primärschlüssel nachträglich hinzufügen (besser: lassen)

ALTER TABLE ist in SQLite ziemlich eingeschränkt. Ein ALTER TABLE ... ADD PRIMARY KEY gibt es schlicht nicht – diesen Befehl kennt SQLite nicht. Hast du den Primärschlüssel vergessen und stehen schon Daten in der Tabelle, hilft nur eins: die Tabelle neu aufbauen.

Das ist der klassische SQLite-Migrations-Tanz. In echtem Code packst du das Ganze in eine Transaktion und deaktivierst kurz die Foreign Keys, falls andere Tabellen auf diese verweisen. Die Lehre daraus: Setz den Primärschlüssel gleich beim CREATE TABLE richtig.

Kurze Checkliste

Wenn du eine neue Tabelle anlegst, frag dich:

  • Hat eine Zeile eine natürliche, eindeutige ID? Falls es ein einzelner Integer ist, nimm INTEGER PRIMARY KEY.
  • Ergibt sich die Identität erst aus einer Kombination von Spalten (typisch für Join-Tabellen)? Dann nutze auf Tabellenebene einen zusammengesetzten Primärschlüssel: PRIMARY KEY (col_a, col_b).
  • Ist der Schlüssel ein Text oder ein anderer Nicht-Integer-Typ? Dann setz explizit NOT NULL dazu.
  • Brauchst du wirklich AUTOINCREMENT? Wahrscheinlich nicht.
  • Ist die Tabelle klein, hauptsächlich lesend und hat einen nicht-ganzzahligen Primärschlüssel? Dann lohnt sich ein Blick auf WITHOUT ROWID (kommt zusammen mit dem rowid-Kapitel).

Als Nächstes: rowid

INTEGER PRIMARY KEY ist hier nur kurz als „Alias für rowid" aufgetaucht — dabei ist die rowid das Fundament unter jeder normalen SQLite-Tabelle und verdient einen eigenen genauen Blick. Genau darum geht es auf der nächsten Seite.

Häufig gestellte Fragen

Wie definiere ich einen Primärschlüssel in SQLite?

Hänge PRIMARY KEY einfach an eine Spalte in deinem CREATE TABLE-Statement, z. B. id INTEGER PRIMARY KEY. Soll der Schlüssel mehrere Spalten umfassen, nutzt du eine Constraint auf Tabellenebene: PRIMARY KEY (col_a, col_b). Die Spalte bzw. die Kombination muss zeilenübergreifend eindeutig sein.

Was ist der Unterschied zwischen INTEGER PRIMARY KEY und anderen Primärschlüsseln?

INTEGER PRIMARY KEY ist ein Sonderfall: Die Spalte wird zum Alias für die eingebaute rowid und liegt direkt im B-Tree der Tabelle – ohne zusätzlichen Index. Jeder andere Typ und jeder zusammengesetzte Schlüssel bekommt einen separaten Unique-Index. Für eine einzelne, numerische ID-Spalte ist INTEGER PRIMARY KEY also schneller und platzsparender.

Brauche ich AUTOINCREMENT bei einem SQLite-Primärschlüssel?

In den meisten Fällen nein. Ein INTEGER PRIMARY KEY vergibt bei einem INSERT mit NULL ohnehin automatisch eine eindeutige rowid. AUTOINCREMENT garantiert dir lediglich zusätzlich, dass IDs nach einem DELETE nie wiederverwendet werden – und das kostet eine extra sqlite_sequence-Tabelle. Lass es weg, außer du brauchst genau dieses streng monoton steigende Verhalten.

Warum erlaubt mein SQLite-Primärschlüssel NULL-Werte?

Das ist ein historischer Bug, der aus Kompatibilitätsgründen erhalten geblieben ist: In normalen Tabellen darf eine Primärschlüsselspalte, die nicht vom Typ INTEGER ist, NULL enthalten – außer du schreibst explizit NOT NULL dazu. Einzige Ausnahme ist INTEGER PRIMARY KEY, das niemals NULL zulässt. Schreib also sicherheitshalber immer NOT NULL an jede PK-Spalte oder nutze eine STRICT-Tabelle, in der die Regel sauber durchgesetzt wird.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S