1. O que é modelagem dimensional?
Modelagem dimensional é uma técnica para projetar data warehouses que coloca simplicidade de consulta e performance acima de eficiência de armazenamento. Criada por Ralph Kimball, segue como padrão porque modela os dados como o negócio pensa.
Por que não usar só 3NF?
Third Normal Form é ótima para OLTP: pouca redundância, sem anomalias de update. Para analytics é péssima: consultas exigem 15+ joins, performance cai e o modelo fica incompreensível para analistas.
Princípio central: separar o “o que aconteceu” do “contexto”
Tabelas fato
Guardam eventos e medidas: pedidos, cliques, pagamentos. Têm métricas agregáveis (SUM, COUNT, AVG).
Tabelas dimensão
Guardam contexto descritivo: quem (clientes), o quê (produtos), quando (datas), onde (locais). Permitem filtro e agrupamento.
Filosofia Kimball
"O data warehouse vale pelo BI que habilita." Modelos dimensionais são feitos para pessoas primeiro, máquinas depois. Se um analista não consegue escrever uma query sem ajuda, o modelo falhou.
2. Fatos vs dimensões
Os blocos fundamentais da modelagem dimensional. Acertando aqui, o resto acompanha.
Tabelas fato
- Registram eventos/transações
- Contêm medidas numéricas (valor, quantidade, duração)
- Possuem chaves estrangeiras para dimensões
- Altas e estreitas (muitas linhas, poucas colunas)
- Grão = uma linha por evento
Exemplos:
fct_orders, fct_page_views, fct_payments
Tabelas dimensão
- Guardam atributos descritivos
- Possuem campos textuais para filtro/agrupamento
- Têm surrogate key como chave primária
- Baixas e largas (poucas linhas, muitas colunas)
- Grão = uma linha por entidade
Exemplos:
dim_customers, dim_products, dim_date
| Pergunta | Se a resposta é... | É uma... |
|---|---|---|
| Dá para fazer SUM/COUNT/AVG? | Sim | Fato |
| Você usaria em GROUP BY ou FILTER? | Sim | Dimensão |
| Descreve uma entidade? | Sim | Dimensão |
| Registra um evento/transação? | Sim | Fato |
Exemplo: pedido e-commerce
-- Tabela fato: uma linha por item de pedido
CREATE TABLE fct_order_lines (
order_line_sk BIGINT PRIMARY KEY, -- Surrogate key
order_id VARCHAR(50), -- Natural key (dimensao degenerada)
customer_sk BIGINT REFERENCES dim_customers,
product_sk BIGINT REFERENCES dim_products,
date_sk INT REFERENCES dim_date,
-- Medidas (agregáveis)
quantity INT,
unit_price DECIMAL(10,2),
discount_amount DECIMAL(10,2),
line_total DECIMAL(10,2)
);
-- Tabela dimensão: uma linha por 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 de star schema
Um star schema coloca a tabela fato no centro, cercada pelas dimensões. O nome vem do formato: fato no meio, dimensões irradiando como uma estrela.
Estrutura Star Schema
dim_date
dim_customer
fct_orders
dim_product
dim_store
Tabela fato no centro, dimensões ao redor — joins simples, consultas intuitivas
Star vs Snowflake Schema
Star schema (preferido)
- • Dimensões desnormalizadas
- • Menos joins = consultas mais rápidas
- • Mais fácil de entender e consultar
- • Alguma redundância (ok para analytics)
Snowflake schema
- • Dimensões normalizadas
- • Mais joins = consultas mais lentas
- • Difícil de usar sem documentação
- • Economiza armazenamento (raramente crítico)
Dica
Quando usar snowflake?
Use snowflake apenas quando a dimensão for realmente hierárquica E analistas consultarem níveis diferentes separadamente (produto → categoria → departamento). Caso contrário, desnormalize na dimensão.
4. Tipos de dimensões
Nem toda dimensão é igual. Conhecer estes padrões evita retrabalho depois.
Conformed dimensions
Compartilhadas por múltiplas tabelas fato. dim_date, dim_customer usadas por fct_orders, fct_page_views, fct_support_tickets. Essenciais para consistência.
Role-playing dimensions
Mesma dimensão usada mais de uma vez com papéis diferentes. dim_date como order_date, ship_date, delivery_date. Use views ou aliases.
Dimensões degeneradas
Chaves de dimensão dentro da tabela fato sem dimensão separada. Números de pedido, IDs de fatura, IDs de transação. Nenhum atributo extra para armazenar.
Junk dimensions
Combine flags de baixa cardinalidade em uma dimensão única. Em vez de 5 booleanos na tabela fato: dim_order_flags com todas as combinações.
Dimensão data
A dimensão mais importante. Atributos pré-calculados: day_of_week, is_weekend, fiscal_quarter, holiday_flag. Use sempre surrogate key inteira (formato YYYYMMDD).
Exemplo: dimensão 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: filtrar/agrupar facilmente por qualquer atributo de 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)
Atributos de dimensões mudam com o tempo. Cliente muda endereço, produto muda categoria, funcionário troca de equipe. Tratar bem essas mudanças é crucial para manter fidelidade histórica.
Tipo 0: Fixo
Nunca muda. Valor original preservado. Use para atributos imutáveis (data de nascimento, data de adesão).
Exemplo: Canal de aquisição original fica mesmo que o cliente retorne por outro canal.
Tipo 1: Overwrite
Valor antigo é sobrescrito. Sem histórico. Use para correções ou atributos sem valor histórico.
Trade-off: Simples, mas você não sabe em que segmento estava no momento do pedido.
Tipo 2: Nova linha (o mais comum)
Cria uma nova linha de dimensão com nova surrogate key. Marca effective_date, expiry_date e flag is_current. Preserva histórico completo.
Ideal para: Atributos onde a história importa. Mudança de segmento, endereço, pricing tier.
Tipo 3: Nova coluna
Adiciona colunas current_value e previous_value. Histórico limitado (geralmente um valor anterior).
Raro: Útil só para comparações “antes/depois”. Tipo 2 quase sempre é melhor.
Exemplo: SCD Tipo 2
-- SCD Tipo 2: cliente muda de 'SMB' para 'Enterprise'
-- ANTES: 1 linha
customer_sk | customer_id | segment | effective_date | expiry_date | is_current
1 | C001 | SMB | 2023-01-01 | 9999-12-31 | TRUE
-- DEPOIS: 2 linhas (antiga expira, nova criada)
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
-- Consulta histórica: qual segmento no momento do pedido?
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 na tabela fato aponta para a versão histórica corretaArmadilha do Tipo 2
No Tipo 2 você decide na carga qual surrogate key associar ao fato. Normalmente quer a versão "current" no momento do evento. Isso exige lookup point-in-time no ETL.
6. Tipos de tabelas fato
Processos diferentes pedem designs diferentes de tabela fato. Escolha o tipo conforme a natureza dos eventos.
Transaction facts
Uma linha por evento no menor grão. O tipo mais comum. Pedidos, cliques, pagamentos, logins.
Grão: Uma linha por item de pedido
Periodic snapshot facts
Uma linha por intervalo de tempo. Captura estado em cadência fixa. Saldos de conta, níveis de estoque, snapshots de pipeline.
Grão: Uma linha por conta por dia
Accumulating snapshot facts
Uma linha por instância de processo, atualizada conforme milestones acontecem. E-commerce fulfillment, pedidos de empréstimo, tickets de suporte.
Grão: Uma linha por pedido (atualizada ao longo do ciclo)
Factless facts
Registram eventos sem medidas—apenas chaves estrangeiras. Presenças de alunos, cobertura de promoções de produto.
Uso: Quem esteve presente? Quais produtos estavam em promoção?
Exemplo: accumulating snapshot (fulfillment)
CREATE TABLE fct_order_fulfillment (
order_sk BIGINT PRIMARY KEY,
order_id VARCHAR(50),
customer_sk BIGINT,
-- Várias chaves de data (milestones)
order_date_sk INT,
payment_date_sk INT,
ship_date_sk INT,
delivery_date_sk INT,
-- Lags calculados
days_to_payment INT,
days_to_ship INT,
days_to_delivery INT,
-- Medidas
order_total DECIMAL(10,2),
current_status VARCHAR(50)
);
-- A linha é atualizada conforme o pedido avança
-- Inicialmente: só order_date_sk
-- Após pagamento: payment_date_sk preenchido, days_to_payment calculado
-- Após envio: ship_date_sk preenchido, days_to_ship calculado
-- Após entrega: delivery_date_sk preenchido, days_to_delivery calculado7. Boas práticas (checklist)
Defina o grão primeiro
Antes de projetar uma tabela fato, escreva o grão: "Uma linha por item de pedido" ou "Uma linha por cliente por dia". Não misture grãos.
Use surrogate keys
Surrogate keys inteiras em todas as dimensões. Natural keys ficam como atributos. Funcionam bem com SCD Tipo 2 e melhoram joins.
Construa conformed dimensions
dim_date e dim_customer devem ser compartilhadas entre todas as tabelas fato. Mesmas chaves, mesmos atributos. Habilita análise cross-processos.
Desnormalize dimensões
Prefira star a snowflake. Inclua categoria, departamento, região direto na dimensão. Armazenamento é barato; joins não.
Adicione uma dimensão data
Nunca faça join em datas cruas. Crie dim_date com atributos pré-calculados. Chaves inteiras (YYYYMMDD) ajudam no partition pruning.
Trate null com linhas padrão
Crie linhas "Unknown" ou "Not Applicable" nas dimensões (SK = -1). Nada de chaves estrangeiras null nas tabelas fato.
Documente o grão
Toda tabela fato deve documentar o grão. Evita double-counting e ajuda analistas a escrever queries corretas.
SCD Tipo 2 para atributos críticos
Segmento de cliente, categoria de produto, departamento—tudo que muda e impacta análise deve ser Tipo 2.
Dica
O “teste do analista”
Depois do design, peça a um analista para escrever 5 queries comuns sem documentação. Se cada uma leva menos de 5 minutos, o modelo está bom. Se precisam de esclarecimentos ou correções, simplifique.
8. Perguntas frequentes
Diferença entre tabela fato e tabela dimensão?
Tabelas fato guardam eventos mensuráveis (transações, cliques, pedidos) com métricas e chaves estrangeiras. Dimensões guardam atributos descritivos (clientes, categorias de produto, datas) para contexto. Fatos respondem "quanto"; dimensões respondem "quem, o quê, quando, onde, por quê".
Star schema vs snowflake schema?
Star schema tem dimensões desnormalizadas ligadas direto à tabela fato, formando uma estrela. Snowflake normaliza dimensões em subdimensões (produto → categoria → departamento). Star é preferido por ser mais simples e performático.
O que são Slowly Changing Dimensions?
SCD tratam mudanças de atributos. Tipo 1 sobrescreve (sem histórico). Tipo 2 cria nova linha com datas de vigência (histórico completo). Tipo 3 adiciona colunas para valores anteriores (histórico limitado). Tipo 2 é o padrão para precisão histórica.
Surrogate key ou natural key?
Use surrogate key (inteiros gerados) como chave primária nas dimensões. Natural keys (ex.: customer_id) como atributos. Surrogate keys são estáveis, rápidas e funcionam com SCD Tipo 2. Natural keys podem mudar e quebrar joins.
Visualize seu modelo dimensional
Crie diagramas star schema claros com fatos, dimensões e relacionamentos. Documente o modelo para que analistas consultem sem pedir ajuda.