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

Cómo crear una dimensión de calendario en SQL: paso a paso

João Barros 05 de July de 2026 5 min de lectura

Una dimensión de calendario es la columna vertebral de cualquier esquema en estrella: permite analizar métricas por año, trimestre, mes o día de la semana sin reescribir la lógica de fechas en cada informe. Al crearla una sola vez y reutilizarla, mantienes números coherentes en todo el data warehouse. Vamos a crear una dimensión de calendario en SQL, paso a paso, y a enlazarla con una tabla de hechos.

Requisitos previos

  • Acceso a una base de datos SQL Server (el ejemplo se adapta a PostgreSQL con pequeños cambios).
  • Permisos para crear tablas e insertar datos.
  • Nociones básicas de esquema en estrella: una tabla de hechos enlazada a tablas de dimensión.

Paso 1: Diseñar las columnas de la dimensión

Antes de escribir SQL, decide qué columnas tendrá la dimensión. La clave DateKey es un entero con formato AAAAMMDD (por ejemplo, 20260705 para el 5 de julio de 2026). Este tipo de clave —llamada smart key— es compacta, ordenable y legible, y hace que las uniones con la tabla de hechos sean más rápidas que comparar valores de fecha. A continuación, añade los atributos que la gente usa para filtrar y agrupar: año, trimestre, mes, nombre del mes, día y un indicador de fin de semana. La regla es sencilla: si el negocio filtra por un atributo, merece una columna.

Paso 2: Crear la tabla DimDate

Crea la tabla con DateKey como clave primaria. Usar tipos pequeños, como tinyint y smallint, mantiene la dimensión ligera y rápida de consultar. Además, una clave entera permite reservar filas especiales —por ejemplo, -1 para una fecha desconocida— sin romper las uniones.

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
);

Paso 3: Generar las fechas con una CTE recursiva

Ahora rellenamos un intervalo de fechas —aquí, de 2015 a 2030— con una CTE recursiva. La primera fila (el ancla) define la fecha inicial; el miembro recursivo suma un día cada vez hasta llegar al final. Cada fila calcula sus atributos a partir de la fecha.

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);

Fíjate en dos detalles. El CASE del fin de semana usa DATEDIFF(DAY, 0, d) % 7, una fórmula independiente del idioma del servidor (5 corresponde a sábado y 6 a domingo). Y OPTION (MAXRECURSION 0) elimina el límite de 100 iteraciones de la CTE, lo que permite generar miles de días de una sola vez.

Las columnas MonthName y DayName heredan el idioma de la sesión de SQL Server. Si necesitas los nombres siempre en un idioma concreto, define el idioma de la sesión o rellena esos textos manualmente.

Paso 4: Enlazar con la tabla de hechos

La tabla de hechos debe guardar la misma DateKey entera, no la fecha completa. Así la unión es rápida y la relación queda explícita mediante una clave foránea.

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

Si vas a consumir el modelo en Power BI, importa DimDate, crea la relación por DateKey y usa Marcar como tabla de fechas, apuntando a FullDate. Solo así funcionan correctamente las funciones de Time Intelligence, como TOTALYTD.

Comprobar el resultado

Confirma que la dimensión se ha rellenado y prueba una agregación por periodo:

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;

La primera consulta debería devolver unos 5.844 días (de 2015 a 2030). La segunda muestra las ventas por año y trimestre, señal de que la dimensión está bien enlazada con la tabla de hechos.

Conclusión

Con apenas unas líneas de SQL ya tienes una dimensión de calendario reutilizable en todo el warehouse. A partir de aquí puedes enriquecerla con año fiscal, número de semana ISO, festivos o columnas de fechas relativas, como "mes actual" o "año anterior". ¿Qué atributo echas más en falta en tus informes? ¿Quizás una columna de festivos por país?