init at work
This commit is contained in:
Executable
+497
@@ -0,0 +1,497 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user