(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como criar uma Fact table de tipo 'Cumulative Balance' em Modelação de Dados (Kimball)

João Barros 12 de November de 2026 5 min de leitura

Este tutorial explica como criar uma Fact table de tipo 'Cumulative Balance' em Modelação de Dados (Kimball) para calcular saldos acumulados por conta/entidade. É útil quando precisa de consultar saldos diários ou mensais sem recalcular toda a história em cada consulta.

Pré-requisitos

  • Conhecimentos básicos de Modelação de Dados (Kimball) e star schema.
  • Ambiente SQL (ex.: SQL Server, Postgres) com tabelas transaccionais de movimentos.
  • Familiaridade com ETL/ELT e com funções de janela (window functions) em SQL.

Passo 1: Entender o caso de uso e o grain

Decida o grain da Fact Cumulative Balance. Ex.: saldo diário por account_id e currency. O grain determina as dimensões de chave e a forma de agregação. Erro comum: escolher grain muito fino (ex.: por transacção) e depois tentar manter saldos acumulados nesse nível, o que complica as actualizações.

Passo 2: Definir as dimensões e atributos

Escolha dimensões conformed que irão ligar à fact: Dimension Account, Dimension Date, Dimension Currency, possivelmente Dimension Branch. Para um grain diário por conta: surrogate keys: account_key, date_key, currency_key. Inclua atributos úteis (account_type, region) na dimensão para filtro e agregação.

Passo 3: Estrutura da Fact table Cumulative Balance

Projete colunas essenciais: surrogate keys das dimensões, balance_amount (o saldo acumulado até à data), opening_balance, period_debit, period_credit, load_date, version_stamp. A Fact deve ser uma tabela de snapshot por data (um snapshot por account_id por date_key).

CREATE TABLE fact_cumulative_balance (
  account_key INT,
  date_key DATE,
  currency_key INT,
  opening_balance DECIMAL(18,2),
  period_debit DECIMAL(18,2),
  period_credit DECIMAL(18,2),
  balance_amount DECIMAL(18,2),
  load_date TIMESTAMP,
  PRIMARY KEY (account_key, date_key, currency_key)
);

Passo 4: Preparar a fonte de movimentos

Consolide a tabela de movimentos (transacções) agrupando por account e data. Inclua débitos e créditos e mapa de keys para as dimensões. Exemplo de agregação diária a partir de transactions_raw.

WITH daily_movements AS (
  SELECT
    a.account_key,
    CAST(t.transaction_date AS DATE) AS date_key,
    t.currency_key,
    SUM(CASE WHEN t.amount >= 0 THEN t.amount ELSE 0 END) AS period_debit,
    SUM(CASE WHEN t.amount < 0 THEN ABS(t.amount) ELSE 0 END) AS period_credit
  FROM transactions_raw t
  JOIN dim_account a ON t.account_id = a.account_id
  GROUP BY a.account_key, CAST(t.transaction_date AS DATE), t.currency_key
)
SELECT * FROM daily_movements;

Passo 5: Calcular saldos acumulados

Use funções de janela para calcular balance_amount como opening_balance + SUM(period_debit - period_credit) ao longo do tempo. Importante: definir correctamente a ordenação por date_key. Erro comum: não reiniciar acumulação por account_key e currency_key.

WITH daily AS (
  SELECT dm.*, a.opening_balance AS opening_balance_base
  FROM daily_movements dm
  LEFT JOIN (
    SELECT account_key, currency_key, MIN(balance_date) AS start_date, opening_balance
    FROM account_opening_balances
    GROUP BY account_key, currency_key, opening_balance
  ) a
  ON dm.account_key = a.account_key AND dm.currency_key = a.currency_key
), cumulative AS (
  SELECT
    account_key,
    date_key,
    currency_key,
    opening_balance_base AS opening_balance,
    period_debit,
    period_credit,
    SUM(period_debit - period_credit) OVER (
      PARTITION BY account_key, currency_key ORDER BY date_key
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) + COALESCE(opening_balance_base,0) AS balance_amount
  FROM daily
)
INSERT INTO fact_cumulative_balance
SELECT account_key, date_key, currency_key, opening_balance, period_debit, period_credit, balance_amount, CURRENT_TIMESTAMP
FROM cumulative;

Passo 6: Atualizações incrementais e correcções

Para cargas incrementais, carregue novas datas e, se existirem alterações em movimentos passados, reaplique a acumulação desde a primeira data afectada. Estratégia prática: manter um ponto de recuperação (watermark) e recalcular por account_key e date_key a partir da data mínima alterada.

-- Exemplo: recalcular a partir de min_changed_date por account
WITH changed_accounts AS (
  SELECT account_key, currency_key, MIN(affected_date) AS recalc_from
  FROM transaction_changes
  GROUP BY account_key, currency_key
)
-- apagar e recalcular para esse intervalo (pseudo-exemplo)
DELETE FROM fact_cumulative_balance f
USING changed_accounts c
WHERE f.account_key = c.account_key
  AND f.currency_key = c.currency_key
  AND f.date_key >= c.recalc_from;

-- depois recriar como no passo 5 apenas para datas >= recalc_from

Verificar o resultado

Valide com queries de soma e checks por account e total global: comparar balance_amount da última data com opening_balance + SUM(period_debit - period_credit) desde o início. Verifique unicidade da chave (account_key, date_key, currency_key) e datas com valores nulos.

-- Verificação básica
SELECT
  account_key,
  MAX(date_key) AS last_date,
  MAX(balance_amount) AS reported_balance,
  (SELECT COALESCE(opening_balance,0) + SUM(period_debit - period_credit)
   FROM fact_cumulative_balance f2
   WHERE f2.account_key = f1.account_key AND f2.currency_key = f1.currency_key
     AND f2.date_key <= f1.date_key) AS recomputed_balance
FROM fact_cumulative_balance f1
GROUP BY account_key, currency_key
HAVING MAX(balance_amount) <> (SELECT COALESCE(opening_balance,0) + SUM(period_debit - period_credit) FROM fact_cumulative_balance f3 WHERE f3.account_key = f1.account_key AND f3.currency_key = f1.currency_key AND f3.date_key <= MAX(f1.date_key));

Conclusão

Uma Fact table Cumulative Balance permite consultas rápidas de saldos históricos sem recalcular toda a história em cada consulta. Próximos passos: automatizar ETL de recalculação incremental, criar índices em (account_key,date_key) e expor em Power BI. Dica: comece por um subconjunto de contas para testar a lógica e monitorizar performance — qual a principal restrição no seu ambiente: I/O ou CPU?