Ce projet démontre l'extraction, transformation et chargement (ETL) de données réelles de lancers MLB (Baseball Savant) pour la série Toronto Blue Jays vs. Colorado Rockies du 4-6 août 2025. L'objectif est de présenter différents exemples de modélisation dimensionnelle.
- src/: Code principal, processus ETL et transformations
- data/: Données brutes et traitées (CSV et Parquet)
- db/: Schémas et base de données DuckDB
- scripts/: Scripts shell pour extraction et export
- requirements.txt: Librairies Python requises
- Makefile: Commandes de gestion du projet
- Python 3.8+
- DuckDB
- Make
# Cloner le projet
git clone https://github.com/philippehamel/presentation-dimensional-data-modeling.git
cd presentation-dimensional-data-modeling
# Configuration de l'environnement
make setup
# Exécution complète du pipeline
make all
# Ou étape par étape
make fetch # Récupération des données
make build # Construction des modèles
make export # Export vers DuckDB
# Ouvrir DuckDB avec les données chargées
make query
# Tables disponibles: star_*, snowflake_*, one_big_table
Structure :
- fact_pitch : Table de faits centrale avec métriques de performance
- dim_player : Dimension joueur (batteur/lanceur) avec informations complètes
- dim_game : Dimension partie avec date, équipes, stade, météo
- dim_count : Dimension compte (balles/prises)
Avantages :
- ✅ Requêtes rapides (moins de jointures)
- ✅ Simplicité de compréhension
- ✅ Performance optimale pour l'analytique
- ✅ Facilité de maintenance
Inconvénients :
- ❌ Redondance de données dans les dimensions
- ❌ Occupation de storage plus importante
- ❌ Mise à jour des dimensions plus complexe
Cas d'usage :
- Tableaux de bord en temps réel
- Analyses ad-hoc fréquentes
- Environnements où la performance de lecture prime
Structure :
- fact_pitch : Table de faits identique au star schema
- dim_player : Dimension joueur normalisée
- dim_position : Table séparée pour les positions
- dim_birth_location : Table séparée pour les lieux de naissance
- dim_game, dim_count : Identiques au star schema
Avantages :
- ✅ Élimination de la redondance
- ✅ Intégrité référentielle renforcée
- ✅ Storage optimisé
- ✅ Maintenance des données de référence facilitée
Inconvénients :
- ❌ Requêtes plus complexes (plus de jointures)
- ❌ Performance dégradée pour certaines analyses
- ❌ Courbe d'apprentissage plus élevée
Cas d'usage :
- Environnements avec contraintes de storage
- Systèmes avec besoins d'intégrité forte
- Applications avec mises à jour fréquentes des données de référence
Structure :
- one_big_table : Toutes les données dans une seule table plate
Avantages :
- ✅ Simplicité maximale (aucune jointure)
- ✅ Performance excellente pour requêtes simples
- ✅ Facilité de compréhension pour utilisateurs non-techniques
- ✅ Compatible avec outils self-service
Inconvénients :
- ❌ Redondance massive
- ❌ Maintenance complexe
- ❌ Inconsistances potentielles
- ❌ Occupation storage très importante
- ❌ Mises à jour coûteuses
Cas d'usage :
- Analyses exploratoires
- Prototypage rapide
- Environnements avec besoins analytiques simples
- Data lakes avec compression efficace
Star Schema :
SELECT
p.full_name,
p.birth_country,
ROUND(AVG(f.launch_speed), 2) as avg_exit_velocity
FROM star_fact_pitch f
JOIN star_dim_player p ON f.player_id_batter_fk = p.player_id
WHERE p.birth_country = 'USA'
AND f.launch_speed IS NOT NULL
GROUP BY p.player_id, p.full_name, p.birth_country
ORDER BY avg_exit_velocity DESC
LIMIT 10;
Snowflake Schema :
SELECT
p.full_name,
bl.birth_country,
ROUND(AVG(f.launch_speed), 2) as avg_exit_velocity
FROM snowflake_fact_pitch f
JOIN snowflake_dim_player p
ON f.player_key_batter = p.player_key
JOIN snowflake_dim_birth_location bl
ON p.location_key = bl.location_key
WHERE bl.birth_country = 'USA'
AND f.launch_speed IS NOT NULL
GROUP BY p.player_key, p.full_name, bl.birth_country
ORDER BY avg_exit_velocity DESC
LIMIT 10;
One Big Table :
SELECT
batter_full_name as full_name,
batter_birth_country as birth_country,
ROUND(AVG(launch_speed), 2) as avg_exit_velocity
FROM one_big_table
WHERE batter_birth_country = 'USA'
AND launch_speed IS NOT NULL
GROUP BY batter, full_name, birth_country
ORDER BY avg_exit_velocity DESC
LIMIT 10;
Dans un ORM comme JPA/Hibernate suivant les meilleures pratiques, cette même requête ressemblerait à :
-- Schéma normalisé traditionnel avec entités style JPA
SELECT
CONCAT(p.firstName, ' ', p.lastName) as fullName,
c.name as birthCountry,
ROUND(AVG(pb.exitVelocity), 2) as avgExitVelocity,
FROM Player p
JOIN Country c ON p.birthCountryId = c.id
JOIN PlayerStatistic ps ON ps.playerId = p.id
JOIN PitchByPitch pb ON pb.batterStatisticId = ps.id
WHERE c.code = 'USA'
AND pb.exitVelocity IS NOT NULL
GROUP BY p.id, p.firstName, p.lastName, c.name
ORDER BY avgExitVelocity DESC
LIMIT 10;
Différences clés :
- JPA/Hibernate : Optimisé pour OLTP, nombreuses tables normalisées, jointures complexes
- Dimensional : Optimisé pour OLAP, moins de jointures, dénormalisation contrôlée
- Performance : L'approche dimensionnelle est 3-10x plus rapide pour l'analytique
- Maintenance : JPA privilégie l'intégrité, dimensional privilégie la performance analytique
Ce projet offre une vue d'ensemble complète de la modélisation dimensionnelle avec des données MLB réelles, démontrant :
- Clarté : La modélisation dimensionnelle accélère le développement et facilite la maintenance
- Flexibilité : Différents modèles pour différents besoins
- Performance : Optimisations pour l'analytique moderne
- Pragmatisme : Approche bottom-up de Kimball adaptée aux technologies cloud
- Granularité : Niveau de détail des faits
- Normalisation : Équilibre entre performance et intégrité
- SCD Types : Gestion de l'historique des dimensions
Le gain de clarté de la modélisation dimensionnelle accélère le développement, facilite la maintenance et promeut la fiabilité.
- Data Warehouse Toolkit de Ralph Kimball link
- Ressources Kimball Group link
- dbt Labs blog de Daniel Poppy link
- Practical Data Modeling blog de Joe Reiss link
- Seattle Data Guy blog de Ben Rogojan link
- Approches non dimensionnelles : Data Vault, Anchor Modeling
- Efficacité storage : 60-80% de réduction de taille grâce à la compression colonnaire
- Performance de chargement : plus rapide dans DuckDB
- Performance de requête : plus rapide pour l'analytique
- Support natif : DuckDB optimisé pour le format Parquet
- Préservation du schéma : Maintient types de données et gestion des nulls
- Simplicité : Lisible, facile à inspecter et déboguer
- Compatibilité universelle : Supporté par virtuellement tous les outils
- Streaming : Lecture ligne par ligne pour très gros datasets
- Tailles importantes : Aucune compression, stockage inefficace
- Performance lente : Overhead de parsing du texte
- Inférence de types : Peut incorrectement deviner les types
- Format binaire : Non lisible, nécessite outils spécialisés
- Complexité : Spécification de format plus complexe
Comparaison tailles de fichiers :
- fact_pitch.csv: 588KB
- fact_pitch.parquet: 225KB (62% plus petit)
- one_big_table.csv: 887KB
- one_big_table.parquet: 264KB (70% plus petit)
Performance de chargement :
- Chargement CSV: 0.910s
- Chargement Parquet: 0.369s (2.5x plus rapide)