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

Como criar uma dimensão de calendário em SQL: passo a passo

João Barros 05 de July de 2026 4 min de leitura

Uma dimensão de calendário é a espinha dorsal de qualquer esquema em estrela: é ela que permite analisar métricas por ano, trimestre, mês ou dia da semana sem reescrever lógica de datas em cada relatório. Ao criá-la uma única vez e reutilizá-la, garante números consistentes em todo o data warehouse. Vamos criar uma dimensão de calendário em SQL, passo a passo, e ligá-la a uma tabela de factos.

Pré-requisitos

  • Acesso a uma base de dados SQL Server (o exemplo adapta-se a PostgreSQL com pequenas alterações).
  • Permissões para criar tabelas e inserir dados.
  • Noções básicas de esquema em estrela: uma tabela de factos ligada a tabelas de dimensão.

Passo 1: Desenhar as colunas da dimensão

Antes de escrever SQL, decida que colunas a dimensão vai ter. A chave DateKey é um inteiro no formato AAAAMMDD (por exemplo, 20260705 para 5 de julho de 2026). Este tipo de chave — a que se chama smart key — é compacta, ordenável e legível, e torna as junções com a tabela de factos mais rápidas do que comparar valores de data. A seguir juntam-se os atributos que as pessoas usam para filtrar e agrupar: ano, trimestre, mês, nome do mês, dia e um indicador de fim de semana. A regra é simples: se o negócio filtra por esse atributo, ele merece uma coluna.

Passo 2: Criar a tabela DimDate

Crie a tabela com a DateKey como chave primária. Usar tipos pequenos, como tinyint e smallint, mantém a dimensão leve e rápida a consultar. Além disso, uma chave inteira permite reservar linhas especiais — por exemplo, -1 para uma data desconhecida — sem partir as junções.

CREATE TABLE DimDate (
    DateKey       int          NOT NULL PRIMARY KEY,
    FullDate      date         NOT NULL,
    YearNumber    smallint     NOT NULL,
    QuarterNumber tinyint      NOT NULL,
    MonthNumber   tinyint      NOT NULL,
    MonthName     varchar(20)  NOT NULL,
    DayNumber     tinyint      NOT NULL,
    DayName       varchar(20)  NOT NULL,
    IsWeekend     bit          NOT NULL
);

Passo 3: Gerar as datas com uma CTE recursiva

Agora preenchemos um intervalo de datas — aqui, de 2015 a 2030 — com uma CTE recursiva. A primeira linha (o âncora) define a data inicial; o membro recursivo soma um dia de cada vez até chegar ao fim. Cada linha calcula os seus atributos a partir da data.

WITH Dates AS (
    SELECT CAST('2015-01-01' AS date) AS d
    UNION ALL
    SELECT DATEADD(DAY, 1, d) FROM Dates WHERE d < '2030-12-31'
)
INSERT INTO DimDate (DateKey, FullDate, YearNumber, QuarterNumber, MonthNumber, MonthName, DayNumber, DayName, IsWeekend)
SELECT
    CONVERT(int, FORMAT(d, 'yyyyMMdd')),
    d,
    YEAR(d),
    DATEPART(QUARTER, d),
    MONTH(d),
    DATENAME(MONTH, d),
    DAY(d),
    DATENAME(WEEKDAY, d),
    CASE WHEN (DATEDIFF(DAY, 0, d) % 7) IN (5, 6) THEN 1 ELSE 0 END
FROM Dates
OPTION (MAXRECURSION 0);

Repare em dois pormenores. O CASE do fim de semana usa DATEDIFF(DAY, 0, d) % 7, uma fórmula independente do idioma do servidor (5 corresponde a sábado e 6 a domingo). E OPTION (MAXRECURSION 0) remove o limite de 100 iterações da CTE, permitindo gerar milhares de dias de uma só vez.

As colunas MonthName e DayName herdam o idioma da sessão de SQL Server. Se precisar de nomes sempre em português, defina a linguagem da sessão ou preencha esses textos manualmente.

Passo 4: Ligar à tabela de factos

A tabela de factos deve guardar a mesma DateKey inteira, e não a data completa. Assim a junção é rápida e a relação fica explícita através de uma chave estrangeira.

ALTER TABLE FactSales
ADD CONSTRAINT FK_FactSales_DimDate
FOREIGN KEY (DateKey) REFERENCES DimDate (DateKey);

Se for consumir o modelo no Power BI, importe a DimDate, crie a relação pela DateKey e use Marcar como Tabela de Datas, apontando para FullDate. Só assim as funções de Time Intelligence, como TOTALYTD, funcionam corretamente.

Verificar o resultado

Confirme que a dimensão ficou preenchida e experimente uma agregação por período:

SELECT COUNT(*) AS TotalDays, MIN(FullDate) AS FirstDate, MAX(FullDate) AS LastDate
FROM DimDate;

SELECT d.YearNumber, d.QuarterNumber, SUM(f.Amount) AS Sales
FROM FactSales f
JOIN DimDate d ON f.DateKey = d.DateKey
GROUP BY d.YearNumber, d.QuarterNumber
ORDER BY d.YearNumber, d.QuarterNumber;

A primeira consulta deve devolver cerca de 5 844 dias (de 2015 a 2030). A segunda mostra as vendas por ano e trimestre — sinal de que a dimensão está bem ligada à tabela de factos.

Conclusão

Com poucas linhas de SQL passa a ter uma dimensão de calendário reutilizável em todo o warehouse. A partir daqui pode enriquecê-la com ano fiscal, número da semana ISO, feriados ou colunas de datas relativas, como "mês atual" ou "ano anterior". Qual é o atributo que mais falta faz nos seus relatórios — talvez uma coluna de feriados por país?