Normal vs. Denormal
Klassische Datenbanktheorie (Codd, 1970): Jeder Datenpunkt genau einmal. Für Kampfhistorien hieße das: fights-Tabelle mit FK auf fighters. Jeder Abruf = JOIN. Bei 1200+ Fightern mit je 10+ Kämpfen = langsam.
JSONB-Vorteile
| Normalisiert (fights-Tabelle) | Denormalisiert (JSONB) |
|---|---|
| JOIN für jeden Abruf | Ein SELECT reicht |
| Schema-Migration für neue Felder | Flexible Felder jederzeit |
| Referentielle Integrität | Kein FK-Overhead |
| Komplexe Queries | JS-Array-Operationen |
| Schnell bei Aggregationen | Schnell bei Einzelabruf |
Analytics über Arrays
Die api/analytics.js zeigt, wie man Statistiken über JSONB in reinem JavaScript berechnet:
fighters.flatMap(f => parseFights(f.tapology_fights))
.filter(fight => fight.result !== 'C')
.reduce((acc, fight) => { ... }, {})flatMap zum Unnesten, reduce für Aggregationen, Map für Gruppierungen. Kein SQL nötig.
Ein echtes tapology_fights Objekt
So sieht ein einzelner Kampfeintrag im JSONB-Array aus. Klick dich durch die Struktur:
INTERAKTIV
tapology_fights — JSON Explorer
Klicke auf Objekte um sie auf- und zuzuklappen. Wähle einen Kampf aus:
{
"result": "W"
,"opponent": "Max Mustermann"
,"event": "Austrian MMA Championship 2025"
,"date": "2025-11-15"
,"method": "TKO (Punches)"
,"round": 2
,"fight_time": "3:41"
,"sport": "MMA"
,"weight_class": "Lightweight"
}
💡 Jeder Kämpfer hat ein JSONB-Array von Kampfeinträgen in dieser Struktur — kein separater Table, kein JOIN.
Denormalisierung ist kein Antipattern — es ist ein bewusstes Trade-off zugunsten von Leseperformance und API-Einfachheit.