I'm building a backend that stores match data across multiple sports (football, basketball, tennis, cricket, MMA, etc.). The problem is these sports have fundamentally different structures:
Football: 2 halves, stoppage time, running score
Basketball: 4 quarters, fouls that affect scoring
Tennis: sets, games, points (15/30/40/deuce) — not a running total
Cricket: innings, wickets, overs
MMA: rounds, no score until decision
I tried a generic match table with nullable columns for every sport, but it grew to 40+ nullable fields and every query needed sport-specific if checks.
I then tried a table-per-sport approach, but that broke the ability to query across sports uniformly.
My current approach:
Shared core table with:
match_id,status,started_at,competition_id,season_idSport-specific payload stored as JSONB column with sport-dependent schema
Shared API layer normalizes the response
Question: Is JSONB the right choice here, or should I use separate tables per sport with a common view? What are the tradeoffs for indexing and query performance when the JSONB fields differ per sport?
Stack: PostgreSQL 15, Node.js backend.