Slowly Changing Dimensions, types 0 à 3
Résumé
Un attribut de dimension change parfois (un client déménage, un produit change de catégorie). Une Slowly Changing Dimension (SCD) est la technique choisie pour décider quoi faire de l’ancienne valeur. Kimball en décrit plusieurs types. Les types 1 et 2 dominent la pratique ; les types 0 et 3 sont plus rares. Type 0 (garder la valeur d’origine), type 1 (écraser, l’historique est perdu), type 2 (ajouter une ligne, l’historique est complet), type 3 (ajouter une colonne pour garder l’ancienne valeur). Le choix se fait attribut par attribut, selon la question métier : veut-on les faits tels que les décrivait le monde à l’époque, ou tels que le décrit le monde aujourd’hui ?
Prérequis
Table de faits, dimension, clé de substitution : Inmon, Kimball et Data Vault et Glossaire data engineering.
Le cas fil rouge
Le client 42, Dupont, habitait Toulouse. Il déménage à Lyon le 2026-03-01. Il a passé une commande en janvier (150 €) et une en avril (200 €).
Question : le chiffre d’affaires par ville de Dupont, c’est Toulouse 150 € et Lyon 200 €, ou Lyon 350 € ?
La réponse dépend du type de SCD.
Type 0 : conserver l’original
L’attribut ne change jamais, quoi qu’il arrive à la source. D’après Kimball, ce type convient aux attributs libellés « original » (par exemple le score de crédit d’origine d’un client), aux identifiants durables, et à la plupart des attributs d’une dimension de date.1
| client_key | client_id | ville_origine |
|---|---|---|
| 1 | 42 | Toulouse |
Après le déménagement, la ligne ne bouge pas. Les faits restent groupés par la valeur d’origine.
Type 1 : écraser
La nouvelle valeur remplace l’ancienne dans la même ligne. Le type 1 reflète toujours l’affectation la plus récente et détruit l’historique. Il est simple à mettre en œuvre et ne crée pas de ligne supplémentaire.1
| client_key | client_id | ville |
|---|---|---|
| 1 | 42 | Lyon |
Les deux commandes sont désormais rattachées à Lyon : 350 €. Conséquence à ne pas oublier : les tables de faits agrégées et les cubes qui dépendent de cet attribut doivent être recalculés.1
Type 2 : ajouter une ligne
Chaque changement crée une nouvelle ligne avec une nouvelle clé de substitution. La clé primaire de la dimension doit donc dépasser la clé naturelle, puisqu’un même client a plusieurs lignes. Kimball demande au minimum trois colonnes supplémentaires : une date d’effet, une date d’expiration et un indicateur de ligne courante.1
| client_key | client_id | ville | valid_from | valid_to | is_current |
|---|---|---|---|---|---|
| 1 | 42 | Toulouse | 2020-01-01 | 2026-02-28 | faux |
| 2 | 42 | Lyon | 2026-03-01 | 9999-12-31 | vrai |
La table de faits utilise la clé de substitution en vigueur au moment du fait : la commande de janvier pointe vers client_key = 1, celle d’avril vers client_key = 2. Résultat : Toulouse 150 €, Lyon 200 €, l’historique tel qu’il était.
flowchart LR F1["Fait : commande de janvier<br/>client_key = 1"] --> D1["Dupont, Toulouse<br/>2020-01-01 → 2026-02-28"] F2["Fait : commande d'avril<br/>client_key = 2"] --> D2["Dupont, Lyon<br/>2026-03-01 → courant"]
Une implémentation SQL (BigQuery)
Deux étapes dans une transaction : fermer les lignes courantes dont l’attribut a changé, puis insérer la nouvelle version.
BEGIN TRANSACTION;
-- 1. Fermer les lignes courantes dont la ville a changé
-- (valid_to = veille de la nouvelle date d'effet)
UPDATE dim_client AS d
SET valid_to = DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY), is_current = FALSE
FROM stg_client AS s
WHERE d.client_id = s.client_id
AND d.is_current
AND d.ville IS DISTINCT FROM s.ville;
-- 2. Insérer la nouvelle version (client modifié ou nouveau)
INSERT INTO dim_client (client_key, client_id, ville, valid_from, valid_to, is_current)
SELECT GENERATE_UUID(), s.client_id, s.ville,
CURRENT_DATE(), DATE '9999-12-31', TRUE
FROM stg_client AS s
LEFT JOIN dim_client AS d
ON d.client_id = s.client_id AND d.is_current
WHERE d.client_id IS NULL;
COMMIT TRANSACTION;Prérequis : stg_client contient une seule ligne par client_id. Avec deux lignes pour un même client, BigQuery refuse l’UPDATE ... FROM (« must match at most one source row »), et l’INSERT créerait deux lignes courantes. Si la source peut en fournir plusieurs, la dédupliquer d’abord, par exemple avec QUALIFY ROW_NUMBER() OVER (PARTITION BY client_id ORDER BY updated_at DESC) = 1.
Après l’étape 1, un client modifié n’a plus de ligne courante : l’étape 2 lui en crée une. Un client jamais vu en reçoit une aussi. Relancer le script sans nouveau changement ne produit aucune ligne : le traitement est idempotent (Idempotence et backfill).
Limites de cette implémentation
Deux changements le même jour donnent une ligne dont
valid_toprécèdevalid_from(intervalle vide) : prévoir une granularité plus fine que le jour si cela peut arriver.client_keyest un UUID dans le script, alors que les tableaux montrent 1 et 2 pour la lisibilité. Une suppression côté source ne ferme pas la ligne. Enfin, la date de validité est celle du traitement. Ici,valid_fromest la date d’exécution du script, pas la date du déménagement. Si la source fournit la date réelle du changement, c’est elle qu’il faut utiliser. Et un changement survenu puis annulé entre deux exécutions n’est jamais vu. C’est la même limite que celle des snapshots dbt : voir dbt et le lakehouse.
Type 3 : ajouter une colonne
La nouvelle valeur écrase l’attribut principal, comme en type 1, et l’ancienne valeur est conservée dans une colonne supplémentaire. Kimball appelle cela une « réalité alternative » : l’utilisateur peut grouper les faits par la valeur courante ou par l’ancienne. La technique est utilisée relativement rarement.1
| client_key | client_id | ville | ville_precedente |
|---|---|---|---|
| 1 | 42 | Lyon | Toulouse |
Elle ne garde qu’un seul niveau d’historique : un second déménagement écrase ville_precedente. Elle convient à un changement massif et unique, par exemple une réorganisation commerciale où l’on veut comparer « avant » et « après ».
Choisir
| Type | Historique | Taille de la dimension | Quand |
|---|---|---|---|
| 0 | Aucun changement | Stable | Valeur d’origine, identifiant durable, date |
| 1 | Perdu | Stable | Correction d’erreur, attribut sans intérêt historique |
| 2 | Complet | Croît avec les changements | Le passé doit rester exact : segment, région, statut |
| 3 | Un niveau | Stable | Comparer « avant » et « après » un changement unique |
Règles d'usage
- Une même dimension mélange des types :
date_naissanceen type 0,villeen type 2.- En cas de doute sur un attribut, la question à poser au métier est « Le rapport d’il y a un an doit-il changer si cet attribut change aujourd’hui ? » Si oui, type 1 ; sinon, type 2.
- Le type 2 multiplie les lignes : à surveiller sur les attributs qui changent souvent.
Les autres types (4, 5, 6, 7)
Variantes décrites par Kimball, rarement utilisées
Kimball décrit huit techniques, de 0 à 7.1 Les types 4 à 7 répondent à des cas particuliers. Intitulés et définitions d’après la documentation Kimball, à approfondir dans The Data Warehouse Toolkit (3ᵉ édition) avant de les mettre en œuvre.
Type Technique Principe 4 Mini-dimension Un groupe d’attributs qui change vite est détaché dans une mini-dimension avec sa propre clé ; la table de faits porte les clés de la dimension de base et de la mini-dimension. Évite la « dimension monstre à changement rapide ». 5 Mini-dimension et outrigger de type 1 Le type 4 enrichi : la dimension de base reçoit la clé de la mini-dimension courante, pour obtenir à la fois la valeur historique et la valeur actuelle. 6 Attributs de type 1 ajoutés à une dimension de type 2 Chaque ligne de type 2 embarque aussi la valeur courante de l’attribut (type 1), écrasée sur toutes les lignes du même client à chaque changement. On peut grouper par la valeur d’époque ou par la valeur actuelle. 7 Dimensions doubles de type 1 et de type 2 Une seule table sert les deux perspectives. La table de faits contient la clé durable et la clé de substitution ; on expose deux vues : une jointe sur la clé durable (valeur courante), l’autre sur la clé de substitution (valeur d’époque). Attention aux sources secondaires : certaines appellent « type 4 » une table d’historique séparée, ce qui n’est pas la définition de Kimball.
Notes liées
- Inmon, Kimball et Data Vault : le modèle dimensionnel qui utilise les SCD.
- Idempotence et backfill : rendre le chargement d’une dimension rejouable ; anachronisme lors d’un backfill.
- dbt et le lakehouse : les snapshots dbt implémentent un type 2.
- Change Data Capture (CDC) : une source naturelle de changements pour alimenter une dimension de type 2.
- Glossaire data engineering : SCD, clé de substitution, grain.
- MOC Data Engineering : carte du vault.