1. Cos’è il dimensional modeling?
Il dimensional modeling è una tecnica di progettazione dei data warehouse che mette semplicità di query e performance davanti all’efficienza di storage. Creato da Ralph Kimball, resta lo standard perché modella i dati come li vede il business.
Perché non usare solo 3NF?
La Third Normal Form è ottima per l’OLTP: minima ridondanza, niente anomalie di update. Per l’analytics è pessima: query con 15+ join, performance bassa e modello incomprensibile per gli analisti.
Principio chiave: separare il “cosa è successo” dal “contesto”
Tabelle dei fatti
Salvano eventi e misure: ordini, clic, pagamenti. Contengono metriche aggregabili (SUM, COUNT, AVG).
Tabelle delle dimensioni
Salvano il contesto descrittivo: chi (clienti), cosa (prodotti), quando (date), dove (sedi). Abilitano filtri e raggruppamenti.
Filosofia Kimball
"Il data warehouse vale quanto la BI che abilita." I modelli dimensionali sono progettati per le persone, non per le macchine. Se un analista non può scrivere query senza aiuto, il modello ha fallito.
2. Fatti vs dimensioni
I mattoni fondamentali del dimensional modeling. Se questi sono corretti, il resto segue.
Tabelle dei fatti
- Registrano eventi/transazioni
- Contengono misure numeriche (importo, quantità, durata)
- Hanno chiavi esterne verso le dimensioni
- Alte e strette (molte righe, poche colonne)
- Grain = una riga per evento
Esempi:
fct_orders, fct_page_views, fct_payments
Tabelle delle dimensioni
- Memorizzano attributi descrittivi
- Contengono campi testuali per filtri/raggruppamenti
- Hanno una surrogate key come chiave primaria
- Basse e larghe (poche righe, molte colonne)
- Grain = una riga per entità
Esempi:
dim_customers, dim_products, dim_date
| Domanda | Se la risposta è... | È una... |
|---|---|---|
| Puoi fare SUM/COUNT/AVG? | Sì | Fatto |
| Lo useresti in GROUP BY o FILTER? | Sì | Dimensione |
| Descrive un’entità? | Sì | Dimensione |
| Registra un evento/una transazione? | Sì | Fatto |
Esempio: ordine e-commerce
-- Tabella dei fatti: una riga per linea d'ordine
CREATE TABLE fct_order_lines (
order_line_sk BIGINT PRIMARY KEY, -- Surrogate key
order_id VARCHAR(50), -- Natural key (dim. degenere)
customer_sk BIGINT REFERENCES dim_customers,
product_sk BIGINT REFERENCES dim_products,
date_sk INT REFERENCES dim_date,
-- Misure (aggregabili)
quantity INT,
unit_price DECIMAL(10,2),
discount_amount DECIMAL(10,2),
line_total DECIMAL(10,2)
);
-- Tabella dimensione: una riga per cliente
CREATE TABLE dim_customers (
customer_sk BIGINT PRIMARY KEY, -- Surrogate key
customer_id VARCHAR(50), -- Natural key
customer_name VARCHAR(255),
email VARCHAR(255),
segment VARCHAR(50), -- 'Enterprise', 'SMB', 'Consumer'
acquisition_channel VARCHAR(50),
created_at TIMESTAMP
);3. Design dello star schema
Uno star schema mette la tabella dei fatti al centro, circondata dalle dimensioni. Il nome viene dalla forma: fatto al centro, dimensioni che irradiano come punte di una stella.
Struttura Star Schema
dim_date
dim_customer
fct_orders
dim_product
dim_store
Tabella dei fatti al centro, dimensioni intorno — join semplici, query intuitive
Star vs Snowflake Schema
Star schema (preferito)
- • Dimensioni denormalizzate
- • Meno join = query più veloci
- • Più facile da capire e interrogare
- • Leggera ridondanza (ok per analytics)
Snowflake schema
- • Dimensioni normalizzate
- • Più join = query più lente
- • Difficile da usare senza documentazione
- • Risparmia storage (raramente importante)
Consiglio
Quando usare lo snowflake?
Snowflake solo quando la dimensione è davvero gerarchica E gli analisti interrogano spesso livelli diversi separatamente (prodotto → categoria → reparto). Altrimenti denormalizza tutto nella dimensione.
4. Tipi di dimensioni
Non tutte le dimensioni sono uguali. Conoscere questi pattern aiuta a modellare bene fin dall’inizio.
Conformed dimensions
Condivise da più tabelle dei fatti. dim_date, dim_customer usate da fct_orders, fct_page_views, fct_support_tickets. Chiave per coerenza aziendale.
Role-playing dimensions
Stessa dimensione usata più volte con significati diversi. dim_date come order_date, ship_date, delivery_date. Usa view o alias.
Dimensioni degenerate
Chiavi di dimensione nella tabella dei fatti senza dimensione separata. Numeri ordine, ID fattura, ID transazione. Nessun attributo da salvare altrove.
Junk dimensions
Combina flag a bassa cardinalità in un’unica dimensione. Invece di 5 boolean nella tabella dei fatti: dim_order_flags con tutte le combinazioni.
Dimensione data
La dimensione più importante. Attributi precalcolati: day_of_week, is_weekend, fiscal_quarter, holiday_flag. Usa sempre surrogate key intere (formato YYYYMMDD).
Esempio: dimensione data
CREATE TABLE dim_date (
date_sk INT PRIMARY KEY, -- formato YYYYMMDD
date_actual DATE NOT NULL,
day_of_week VARCHAR(10), -- 'Monday', 'Tuesday', ...
day_of_week_num INT, -- 1-7
day_of_month INT,
day_of_year INT,
week_of_year INT,
month_num INT,
month_name VARCHAR(10),
quarter_num INT,
quarter_name VARCHAR(10), -- 'Q1', 'Q2', ...
year_num INT,
fiscal_year INT,
fiscal_quarter INT,
is_weekend BOOLEAN,
is_holiday BOOLEAN,
holiday_name VARCHAR(50)
);
-- Uso: filtrare/raggruppare facilmente per ogni attributo data
SELECT
d.month_name,
d.year_num,
SUM(f.order_total) as revenue
FROM fct_orders f
JOIN dim_date d ON f.order_date_sk = d.date_sk
WHERE d.is_weekend = FALSE
GROUP BY d.month_name, d.year_num;5. Slowly Changing Dimensions (SCD)
Gli attributi delle dimensioni cambiano nel tempo. Un cliente cambia indirizzo, un prodotto viene riclassificato, un dipendente cambia reparto. Gestirli correttamente è cruciale per la fedeltà storica.
Tipo 0: Fisso
Non cambia mai. Valore originale preservato. Usa per attributi immutabili (data iscrizione, data di nascita).
Esempio: Il canale di acquisizione originario resta anche se il cliente torna da un altro canale.
Tipo 1: Overwrite
Il valore vecchio viene sovrascritto. Nessuna storia. Usa per correzioni o attributi senza valore storico.
Trade-off: Semplice, ma non puoi rispondere a “in che segmento era al momento dell’ordine?”.
Tipo 2: Nuova riga (il più comune)
Crea una nuova riga di dimensione con nuova surrogate key. Traccia con effective_date, expiry_date, flag is_current. Storia completa preservata.
Ideale per: Attributi dove conta la storia. Cambio segmento, indirizzo, pricing tier.
Tipo 3: Nuova colonna
Aggiunge colonne current_value e previous_value. Storia limitata (tipicamente un valore precedente).
Raro: Utile solo per confronti “prima/dopo”. Il tipo 2 è quasi sempre migliore.
Esempio: SCD Tipo 2
-- SCD Tipo 2: il cliente passa da 'SMB' a 'Enterprise'
-- PRIMA: 1 riga
customer_sk | customer_id | segment | effective_date | expiry_date | is_current
1 | C001 | SMB | 2023-01-01 | 9999-12-31 | TRUE
-- DOPO: 2 righe (vecchia scade, nuova aggiunta)
customer_sk | customer_id | segment | effective_date | expiry_date | is_current
1 | C001 | SMB | 2023-01-01 | 2024-06-15 | FALSE
2 | C001 | Enterprise | 2024-06-15 | 9999-12-31 | TRUE
-- Query storica: quale segmento al momento dell'ordine?
SELECT
o.order_id,
c.segment as segment_at_order_time
FROM fct_orders o
JOIN dim_customers c ON o.customer_sk = c.customer_sk
-- customer_sk nella tabella dei fatti punta alla versione storica correttaInsidia del Tipo 2
Con il Tipo 2 devi decidere in fase di load quale surrogate key assegnare al fatto. Di solito vuoi la versione "current" al momento dell’evento. Serve un lookup point-in-time nell’ETL.
6. Tipi di tabelle dei fatti
Processi diversi richiedono design diversi della tabella dei fatti. Scegli il tipo in base alla natura degli eventi.
Transaction facts
Una riga per evento al grain più basso. Il tipo più comune. Ordini, clic, pagamenti, login.
Grain: Una riga per linea d’ordine
Periodic snapshot facts
Una riga per intervallo temporale. Cattura lo stato a intervalli regolari. Saldi conto, livelli inventario, snapshot pipeline.
Grain: Una riga per account per giorno
Accumulating snapshot facts
Una riga per istanza di processo, aggiornata man mano che avvengono i milestone. Evasione ordini, richieste di prestito, ticket di supporto.
Grain: Una riga per ordine (aggiornata nel ciclo di vita)
Factless facts
Registrano eventi senza misure—solo chiavi esterne. Presenze studenti, copertura promozioni prodotto.
Uso: Chi era presente? Quali prodotti erano in promo?
Esempio: accumulating snapshot (evasione ordine)
CREATE TABLE fct_order_fulfillment (
order_sk BIGINT PRIMARY KEY,
order_id VARCHAR(50),
customer_sk BIGINT,
-- Più chiavi data (milestone)
order_date_sk INT,
payment_date_sk INT,
ship_date_sk INT,
delivery_date_sk INT,
-- Lag calcolati
days_to_payment INT,
days_to_ship INT,
days_to_delivery INT,
-- Misure
order_total DECIMAL(10,2),
current_status VARCHAR(50)
);
-- La riga si aggiorna quando l'ordine avanza
-- Inizialmente: solo order_date_sk
-- Dopo il pagamento: payment_date_sk compilato, days_to_payment calcolato
-- Dopo la spedizione: ship_date_sk compilato, days_to_ship calcolato
-- Dopo la consegna: delivery_date_sk compilato, days_to_delivery calcolato7. Best practice (checklist)
Definisci prima il grain
Prima di progettare una tabella dei fatti, scrivi il grain: "Una riga per linea d’ordine" o "Una riga per cliente per giorno". Non mescolare i grain.
Usa surrogate key
Surrogate key intere in tutte le dimensioni. Le natural key restano attributi. Gestiscono bene SCD Tipo 2 e migliorano le join.
Costruisci conformed dimensions
dim_date e dim_customer devono essere condivise tra tutte le tabelle dei fatti. Stesse chiavi, stessi attributi. Abilita analisi cross-process.
Denormalizza le dimensioni
Preferisci star a snowflake. Includi categoria, reparto, regione direttamente nella dimensione. Lo storage è economico, le join no.
Aggiungi una dimensione data
Mai join su date raw. Crea dim_date con attributi precalcolati. Chiavi intere (YYYYMMDD) per il partition pruning.
Gestisci i null con righe di default
Crea righe "Unknown" o "Not Applicable" nelle dimensioni (SK = -1). Niente chiavi esterne null nelle tabelle dei fatti.
Documenta il grain
Ogni tabella dei fatti deve documentare il grain. Previene il double-counting e aiuta gli analisti a scrivere query corrette.
SCD Tipo 2 per attributi critici
Segmento cliente, categoria prodotto, reparto—tutto ciò che cambia e influenza l’analisi dovrebbe essere Tipo 2.
Consiglio
L’"analyst test"
Dopo il design, chiedi a un analista di scrivere 5 query comuni senza documentazione. Se ciascuna richiede meno di 5 minuti, il modello è buono. Se servono chiarimenti o correzioni, semplifica.
8. Domande frequenti
Differenza tra tabella dei fatti e tabella delle dimensioni?
Le tabelle dei fatti memorizzano eventi misurabili (transazioni, clic, ordini) con metriche e chiavi esterne. Le dimensioni memorizzano attributi descrittivi (clienti, categorie prodotto, date) per dare contesto. I fatti rispondono a "quanto", le dimensioni a "chi, cosa, quando, dove, perché".
Star schema vs snowflake schema?
Uno star schema ha dimensioni denormalizzate collegate direttamente alla tabella dei fatti, formando una stella. Uno snowflake normalizza le dimensioni in sotto-dimensioni (prodotto → categoria → reparto). Lo star è preferito perché più semplice e performante.
Cosa sono le Slowly Changing Dimensions?
Le SCD gestiscono cambiamenti di attributo. Tipo 1 sovrascrive (nessuna storia). Tipo 2 aggiunge una nuova riga con date di validità (storia completa). Tipo 3 aggiunge colonne per valori precedenti (storia limitata). Il tipo 2 è lo standard per accuratezza storica.
Surrogate key o natural key?
Usa surrogate key (interi auto-generati) come chiavi primarie nelle dimensioni. Le natural key (ad es. customer_id) come attributi. Le surrogate key sono stabili, performanti e funzionano con SCD Tipo 2. Le natural key possono cambiare e rompere le join.
Visualizza il tuo modello dimensionale
Crea diagrammi star schema chiari con fatti, dimensioni e relazioni. Documenta il modello così gli analisti possono fare query senza chiedere aiuto.