#datenbank ➡️ [[#Zusammenfassung]] --- ## 1. Grundidee: Was ist eine Identity Column? Stell dir eine Tabelle „Kunden“ vor. Jeder Kunde soll eine eindeutige Nummer bekommen: - Kunde 1 - Kunde 2 - Kunde 3 - … Du willst diese Nummern **nicht selbst vergeben**, sondern die Datenbank soll das **automatisch** machen, wenn du einen neuen Datensatz einfügst. Genau das ist eine **Identity Column**: > Eine Identity Column ist eine Spalte, deren Wert von der Datenbank automatisch erzeugt wird, meist als laufende Nummer (1, 2, 3, …). Sie wird oft als Primärschlüssel benutzt. Typische Eigenschaften: - numerisch (z. B. `INT`, `BIGINT`) - beim `INSERT` wird kein Wert angegeben – die DB füllt ihn selbst - der Wert ist pro Tabelle eindeutig - er steigt (meist) monoton an (1,2,3,…), kann aber Lücken haben --- ## 2. Ein einfaches Beispiel Beispiel in **SQL Server**: ```sql CREATE TABLE Kunde ( KundeID INT IDENTITY(1,1) PRIMARY KEY, -- Start bei 1, Inkrement 1 Name NVARCHAR(100) NOT NULL, Email NVARCHAR(200) NOT NULL ); -- Einfügen ohne KundeID anzugeben: INSERT INTO Kunde (Name, Email) VALUES ('Anna Beispiel', 'anna@example.com'), ('Max Muster', 'max@muster.de'); -- Auslesen: SELECT * FROM Kunde; ``` Ergebnis (vereinfacht): ```text KundeID | Name | Email --------+----------------+------------------- 1 | Anna Beispiel | anna@example.com 2 | Max Muster | max@muster.de ``` Du hast `KundeID` nie selbst gesetzt – die Datenbank hat sie erzeugt. --- ## 3. Abgrenzung zu verwandten Begriffen ### 3.1 Identity Column vs. Primärschlüssel - **Primärschlüssel (Primary Key)**: Konzept: eine oder mehrere Spalten, die **eindeutig** einen Datensatz identifizieren. - **Identity Column**: Mechanismus: eine Spalte, deren Werte automatisch generiert werden. Eine Identity Column **kann** der Primärschlüssel sein, muss aber nicht. Man kann auch: - Identity Column + separater fachlicher Schlüssel (z. B. Kundennummer im ERP) - Primärschlüssel auf einer anderen Spalte (z. B. E-Mail) ohne Identity Column In der Praxis: Sehr häufig ist die Identity Column = Primärschlüssel. --- ### 3.2 Identity Column vs. Auto-Increment Viele Systeme benutzen unterschiedliche Begriffe: - SQL Server: `IDENTITY` - PostgreSQL: `GENERATED AS IDENTITY` (früher `SERIAL`) - MySQL: `AUTO_INCREMENT` - Oracle: Identity-Spalten oder separate `SEQUENCE` **Inhaltlich** ist „Identity Column“ oft gleichbedeutend mit „Auto-Increment-Spalte“: eine automatisch hochzählende Spalte. PostgreSQL-Beispiel: ```sql CREATE TABLE kunde ( kunde_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL ); ``` MySQL-Beispiel: ```sql CREATE TABLE kunde ( kunde_id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(200) NOT NULL ); ``` --- ### 3.3 Identity Column vs. Sequence **Sequence (Sequenz)** ist ein **eigenständiges Datenbankobjekt**, das Zahlen generiert, etwa so: ```sql CREATE SEQUENCE kunde_seq START WITH 1 INCREMENT BY 1; SELECT nextval('kunde_seq'); -- gibt 1 zurück SELECT nextval('kunde_seq'); -- gibt 2 zurück ``` Eine Identity Column ist oft intern an eine Sequence gekoppelt. Unterschied: - Sequence: unabhängig von Tabellen, du kannst sie überall verwenden - Identity Column: an genau eine Spalte gebunden, automatischer Einsatz bei `INSERT` In einigen DB-Systemen (z. B. Oracle, PostgreSQL) sind Identity Columns technisch eine bequeme Hülle um eine Sequence. --- ### 3.4 Identity Column vs. natürlicher/fachlicher Schlüssel - **Natürlicher/fachlicher Schlüssel**: basiert auf echten Geschäfts-Daten Beispiele: - E-Mail-Adresse als eindeutiger Schlüssel für Benutzer - ISBN für Bücher - Personalnummer aus dem HR-System - **Surrogate Key** (hier passt die Identity Column): technischer, künstlicher Schlüssel, ohne fachliche Bedeutung. Identity Columns sind **Surrogate Keys**: - `KundeID = 42` sagt fachlich nichts über den Kunden aus - sie existiert nur, um Zeilen eindeutig zu identifizieren In vielen Projekten arbeitet man mit: - Identity Column als Primärschlüssel - dazu Unique-Constraints auf fachlichen Spalten (z. B. `Email UNIQUE`) --- ### 3.5 Identity Column vs. UUID / GUID Statt einer Identity Column (INT) kann man auch **UUIDs** (z. B. `UUID`, `uniqueidentifier`) als Primärschlüssel verwenden. Unterschiede: - **Identity (INT/BIGINT)**: - kleiner, effizienter Index - gut lesbar (`KundeID = 123`) - nicht global eindeutig über mehrere Systeme, nur innerhalb der Tabelle - lässt leicht Rückschlüsse auf Anzahl/Abfolge (z. B. „wir sind bei Kunde 10.000“) - **UUID**: - sehr große, „zufällig“ wirkende Zeichenfolge - global eindeutig (sehr hoch wahrscheinlich) - schwerer zu merken/lesen - Indexe sind meist größer, Einfügen kann teurer sein Z. B. in PostgreSQL: ```sql CREATE TABLE kunde ( kunde_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL ); ``` Identity Column und UUID sind also alternative Strategien zur ID-Erzeugung. --- ## 4. Welche Probleme löst eine Identity Column? ### 4.1 Eindeutige Identifikation von Datensätzen Jede Zeile bekommt automatisch eine eindeutige ID: - kein Risiko, dass zwei Datensätze „versehentlich“ dieselbe ID haben - Voraussetzung für saubere Joins, Fremdschlüssel, Referenzen Beispiel: Fremdschlüssel von Auftrag auf Kunde: ```sql CREATE TABLE Kunde ( KundeID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(100) NOT NULL ); CREATE TABLE Auftrag ( AuftragID INT IDENTITY(1,1) PRIMARY KEY, KundeID INT NOT NULL, Betrag DECIMAL(10,2) NOT NULL, FOREIGN KEY (KundeID) REFERENCES Kunde(KundeID) ); ``` Beim Anlegen eines Auftrags verweist `Auftrag.KundeID` auf `Kunde.KundeID`. --- ### 4.2 Vereinfachung beim Einfügen von Daten Ohne Identity Column müsstest du bei jedem `INSERT` einen neuen eindeutigen Wert berechnen und eintragen – in Mehrbenutzer-Umgebungen ist das fehleranfällig. Mit Identity Column: ```sql INSERT INTO Kunde (Name) VALUES ('Lisa Test'); -- ID macht die DB ``` Du musst dir keine Gedanken machen über: - die nächste freie Nummer - Race Conditions (zwei Nutzer generieren zufällig dieselbe ID) - Sperren von Tabellen, etc. --- ### 4.3 Unterstützung von Mehrbenutzerbetrieb In einer Datenbank arbeiten meist viele Nutzer oder Prozesse parallel. Identity Columns sind so implementiert, dass: - gleichzeitig eingefügte Zeilen **ohne Kollision** IDs bekommen - du dich nicht um Synchronisation kümmern musst Die Datenbank regelt intern: - Sperren der Sequenz - Transaktionssicherheit --- ### 4.4 Gute Performance und einfache Indizierung Numerische Identity-Spalten: - sind kompakt (z. B. 4 oder 8 Byte) - lassen sich effizient indexieren - wachsen meist monoton -> B-Tree-Indizes funktionieren sehr performant Im Vergleich: - komplexe Primärschlüssel aus mehreren Textspalten sind größer, langsamer - Identity-Spalte als „technischer Schlüssel“ macht viele Operationen schneller --- ## 5. Herausforderungen und typische Fallstricke ### 5.1 Lücken in der Nummernfolge Anfänger erwarten oft: „Wenn ich einen Datensatz lösche oder eine Transaktion zurückrolle, füllt die DB die Nummer wieder auf.“ Das **passiert nicht** (und sollte auch nicht passieren). Gründe für Lücken: - Transaktion wird zurückgerollt nach dem Erzeugen einer ID - Datensatz mit ID 10 wird gelöscht - Parallel eingefügte Datensätze Beispiel: - du fügst einen Datensatz ein -> bekommt ID 10 - im gleichen Moment ein zweiter -> ID 11 - deine Transaktion schlägt fehl -> Datensatz 10 existiert nicht - Datensatz 11 bleibt erhalten Ergebnis: Es gibt jetzt mitunter keinen Datensatz mit ID 10. Das ist normal. **Wichtige Praxisregel**: Eine Identity Column ist eine **technische ID**, keine „lückenlose Rechnungsnummer“, keine „fortlaufende Kundennummer“ im rechtlichen Sinn. Wenn du wirklich lückenlose Nummern brauchst (z. B. für Rechnungen), wird das typischerweise anders gelöst (spezialisierte Logik, Sperren, eigene Tabellen). --- ### 5.2 „ID = Reihenfolge“ ist gefährlich Menschen neigen dazu zu denken: > „ID 100 wurde nach ID 99 erstellt.“ In der Praxis ist das oft, aber **nicht garantiert**: - Backups/Restores - Replikation - Imports aus anderen Systemen - unterschiedliche Identity-Strategien Besser: Wenn du die zeitliche Reihenfolge brauchst, verwende eine **Zeitstempel-Spalte**: ```sql CREATE TABLE Kunde ( KundeID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(100), CreatedAt DATETIME2 NOT NULL DEFAULT SYSDATETIME() ); ``` Dann sortierst du nach `CreatedAt`, nicht nach `KundeID`. --- ### 5.3 Limits des Datentyps (Überlauf) Wenn du `INT` (32-Bit) verwendest, ist irgendwann Schluss (ca. 2,1 Milliarden positive Werte). Bei sehr großen Tabellen oder sehr intensiver Nutzung kann das relevant werden. Praktischer Tipp: - lieber direkt `BIGINT` für Identity-Spalten verwenden – die Grenze ist so hoch, dass du in normalen Anwendungen nicht anstößt. Beispiel: ```sql CREATE TABLE Bestellung ( BestellungID BIGINT IDENTITY(1,1) PRIMARY KEY, ... ); ``` --- ### 5.4 Datenmigrationen, Importe, Reseeding Problemfälle: - du willst Daten von einem System ins andere migrieren - dort gibt es schon Datensätze mit Identity-Werten - beim Import dürfen keine IDs kollidieren Strategien: 1. **Identity-Werte mit übernehmen** In vielen DB-Systemen kann man temporär eigene Werte setzen: SQL Server: ```sql SET IDENTITY_INSERT Kunde ON; INSERT INTO Kunde (KundeID, Name, Email) VALUES (1001, 'Imported User', 'import@example.com'); SET IDENTITY_INSERT Kunde OFF; ``` 2. **Nach dem Import die Identity „neu starten“ (reseeden)** SQL Server: ```sql DBCC CHECKIDENT ('Kunde', RESEED, 2000); -- nächste ID = 2001 ``` Diese Themen werden wichtig, wenn du Daten zwischen Systemen hin- und herschiebst. --- ### 5.5 Verteilte Systeme / Sharding In modernen Architekturen gibt es manchmal: - mehrere Datenbankserver (Shards) - die später zusammengeführt werden sollen Wenn jede Tabelle eine Identity-Spalte bei 1 beginnen lässt, kann es bei Merge-Vorgängen Kollisionen geben (z. B. `KundeID = 100` existiert auf zwei Servern mit unterschiedlichen Kunden). Lösungen: - pro Server unterschiedliche Startwerte und Inkremente: - Server A: `IDENTITY(1, 3)` → 1,4,7,… - Server B: `IDENTITY(2, 3)` → 2,5,8,… - Server C: `IDENTITY(3, 3)` → 3,6,9,… - oder Nutzung von UUIDs statt Identity-Spalten --- ### 5.6 Informationsleck (Datenschutz, Sicherheit) Außen sichtbare IDs (z. B. in URLs) können Informationen verraten: - wenn dein Kunde eine URL wie `/bestellung/1000` sieht, kann er ahnen, dass es ~1000 Bestellungen gibt - er kann versuchen, `/bestellung/999` aufzurufen (ID-Raten) Das ist kein Problem der Identity-Spalte an sich, sondern der Entscheidung, **diese ID außerhalb der Anwendung sichtbar** zu machen. Lösungen: - Zugriffskontrollen („zeige nur eigene Bestellungen“) - andere, „nicht-erratbare“ IDs für externe Darstellung (z. B. UUIDs, Hashes) --- ### 5.7 Portabilität zwischen Datenbanksystemen Jede Datenbank hat leicht andere Syntax: - SQL Server: `INT IDENTITY(1,1)` - PostgreSQL: `GENERATED ALWAYS AS IDENTITY` - MySQL: `AUTO_INCREMENT` - Oracle: `GENERATED BY DEFAULT AS IDENTITY` oder Sequences Wenn du Wert auf **Portabilität** legst (dass dein Schema in mehreren DB-Systemen läuft), musst du darauf achten: - eher Standard-SQL (`GENERATED [ALWAYS|BY DEFAULT] AS IDENTITY`) - oder ID-Generierung in der Anwendung selbst --- ## 6. Praxisnahe Gesamtbeispiele ### 6.1 Einfache Kunden- und Bestellverwaltung (SQL Server-Variante) ```sql CREATE TABLE Kunde ( KundeID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(100) NOT NULL, Email NVARCHAR(200) NOT NULL UNIQUE, CreatedAt DATETIME2 NOT NULL DEFAULT SYSDATETIME() ); CREATE TABLE Bestellung ( BestellungID INT IDENTITY(1,1) PRIMARY KEY, KundeID INT NOT NULL, Datum DATETIME2 NOT NULL DEFAULT SYSDATETIME(), Betrag DECIMAL(10, 2) NOT NULL, FOREIGN KEY (KundeID) REFERENCES Kunde(KundeID) ); -- Neuen Kunden anlegen: INSERT INTO Kunde (Name, Email) VALUES ('Anna Beispiel', 'anna@example.com'); -- Die ID des eben eingefügten Kunden holen: SELECT SCOPE_IDENTITY() AS NeueKundeID; ``` `SCOPE_IDENTITY()` gibt dir in SQL Server die zuletzt erzeugte Identity in der aktuellen Session und dem aktuellen Scope. Damit kannst du *direkt danach* eine Bestellung für genau diesen Kunden anlegen. --- ### 6.2 Beispiel in PostgreSQL (moderner Standard) ```sql CREATE TABLE kunde ( kunde_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE bestellung ( bestellung_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, kunde_id BIGINT NOT NULL REFERENCES kunde(kunde_id), datum TIMESTAMPTZ NOT NULL DEFAULT now(), betrag NUMERIC(10,2) NOT NULL ); -- Einfügen: INSERT INTO kunde (name, email) VALUES ('Max Muster', 'max@muster.de') RETURNING kunde_id; -- PostgreSQL-spezifisch, um die ID direkt zu bekommen ``` --- ## Zusammenfassung - Eine **Identity Column** ist eine Spalte, deren Wert die Datenbank automatisch erzeugt (typisch: laufende Nummer). - Sie wird häufig als **Primärschlüssel** verwendet, ist aber **nicht dasselbe** wie der Begriff „Primärschlüssel“. - Sie löst v. a. diese Probleme: - eindeutige Identifikation von Zeilen - einfache Inserts, ohne selbst IDs zu erzeugen - sichere Nutzung in Mehrbenutzer-Umgebungen - gute Performance bei Indexen und Joins - Sie ist verwandt mit: - Auto-Increment-Spalten (`AUTO_INCREMENT`, `SERIAL`) - Sequences (`SEQUENCE`) - Surrogate Keys im Gegensatz zu natürlichen Schlüsseln - Alternativen wie UUID/GUID - Typische Herausforderungen: - Lücken in der Nummernfolge (normal und erwünscht) - ID ist nicht zuverlässig die zeitliche Reihenfolge - Datenmigration, Reseeding, Sharding - potenzielles Informationsleck, wenn IDs öffentlich sichtbar sind - Unterschiede in der Implementierung zwischen Datenbanksystemen