[Data Modeling] ¿Como hago una Tabla Fecha?
Durante el desarrollo de un modelo Bi es indispensable contar con una tabla fecha. Aun en las diferentes teorías de bi la importancia de una tabla que nos congregue día a día las filas con columnas de años, mes, semana, trimestre, etc. a disponibilidad de uso es crítico para un buen desarrollo de tableros o end user bi. Hoy en día, en microsoft particularmente, los modelos tabulares son un lugar indispensable para tener una tabla fecha. Los mismos traen gran capacidad de funciones de fecha para cálculos complejos en dax. Estos cálculos los denominan Time Intelligence. Para no desviarnos más con el tema vamos a ver como y donde puedo crear esta tabla.
SQL Server
Podemos generar un procedimiento almacenado que corra para reconstruirla durante las noches o en cada llamada de la actualización del modelo tabular.
Tengamos en cuenta que el SELECT solo retorna una tabla de una columna con fechas desde hasta que hayamos seteado. Vamos a ver el panorama completo, luego cada quien separará en un procedimiento almacenado o lo usará de consulta directa.
CREATE TABLE dim_date
(
[date] DATE PRIMARY KEY,
[year] AS DATEPART(YEAR, [date]),
[month] AS DATEPART(MONTH, [date]),
[MonthName] AS DATENAME(MONTH, [date]),
[day] AS DATEPART(DAY, [date]),
FirstOfMonth AS CONVERT(DATE, DATEADD(MONTH, DATEDIFF(MONTH, 0, [date]), 0)),
FirstOfYear AS CONVERT(DATE, DATEADD(YEAR, DATEDIFF(YEAR, 0, [date]), 0)),
[week] AS DATEPART(WEEK, [date]),
[ISOweek] AS DATEPART(ISO_WEEK, [date]),
[DayOfWeek] AS DATEPART(WEEKDAY, [date]),
[quarter] AS DATEPART(QUARTER, [date]),
[datekey] AS CONVERT(CHAR(8), [date], 112),
[dmy] AS CONVERT(CHAR(10), [date], 101)
);
Procedimiento:
TRUNCATE TABLE dim_date
SET DATEFORMAT dmy;
SET LANGUAGE Spanish;DECLARE @StartDate DATE = '20000101'
DECLARE @CutoffDate DATE = GETDATE()+1 INSERT #dim([date])
SELECT d
FROM
(
SELECT d = DATEADD(DAY, rn - 1, @StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, @StartDate, @CutoffDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
-- on my system this would support > 5 million days
ORDER BY s1.[object_id]
) AS x
) AS y;SELECT * FROM dim_date
Power Query
No siempre tendremos la posibilidad de nutrirnos de un acceso de escritura a una base de datos para poder armar la tabla fecha de la primera forma mencionada, sino que a veces vamos a tener que terminar el data modeling incorporando la fecha en la etapa de capa semántica junto al modelo tabular. Éste método es el más usado en mi opinión si construimos Bi como servicio a entidades. Lo más atractivo es tener una tabla fecha resulta antes de cargar y aplicar el modelo tabular.
Tengamos en cuenta que la fecha de inicio esta seteada a mano hasta fecha actual igual que en SQL.
= let
//Set start date
StartDate = #date(2010, 1, 1),
EndDate = DateTime.Date(DateTime.LocalNow()),
//Get the number of dates that will be required for the table
GetDateCount = Duration.Days(EndDate - StartDate),
//Take the count of dates and turn it into a list of dates
GetDateList = List.Dates(StartDate, GetDateCount,
#duration(1,0,0,0)),
//Convert the list into a table
DateListToTable = Table.FromList(GetDateList,
Splitter.SplitByNothing(), {"Fecha"}, null, ExtraValues.Error),
// Convert to Date
ConvertToDate = Table.TransformColumns(DateListToTable,{{"Fecha", Date.From, type date}}),
// Date Key
DateKey = Table.AddColumn(ConvertToDate, "IdFecha", each Number.FromText(Text.From(Date.Year([Fecha])) & Date.ToText([Fecha],"MM") & Date.ToText([Fecha],"dd") ) , Int32.Type ),
//Add Year Column
YearNumber = Table.AddColumn(DateKey, "Año",
each Date.Year([Fecha]), Int32.Type),
//Add Quarter format QX
Quarter = Table.AddColumn(YearNumber, "Trimestre",
each Text.Combine({"Q", Text.From(Date.QuarterOfYear([Fecha]))}), type text),
//Add Month Number Column
MonthNumber = Table.AddColumn(Quarter , "IdMes",
each Date.Month([Fecha]), Int32.Type),
//Add Month Name Column
MonthName = Table.AddColumn(MonthNumber , "Mes",
each Date.ToText([Fecha],"MMMM", "es-es"), type text),
//Add Period
Periodo = Table.AddColumn(MonthName , "Periodo",
each Text.From([Año]) & Text.PadStart(Text.From([IdMes]),2,"0"), type text),
//Add YearQ
TrimestreAño = Table.AddColumn(Periodo, "TrimestreAño",
each Text.Combine({Text.From([Año]), [Trimestre]}), type text ),
//Add Day Number Column
DayNumber = Table.AddColumn(TrimestreAño , "Dia",
each Date.Day([Fecha]), Int32.Type),
//Add Day Name Column
DayName = Table.AddColumn(DayNumber , "Nombre Dia",
each Date.ToText([Fecha], "dddd"), type text)
in
DayName
DAX
Al igual que el paso anterior las opciones de modelado puede acotarse más según el desarrollo y los permisos que nos estén concediendo. Entonces no debemos dejar de lado la posibilidad de construir una tabla fecha una vez ya existente el modelo tabular. La principal ventaja de éste método es construir la tabla fecha a partir de fechas del mismo modelo creado y no necesariamente predefinidas de la forma (hoy y últimos N días/meses/años). De esta forma puedo crearla a partir de la mínima y máxima fecha de mi tabla de hecho.
En caso de dax podemos nutrir la fecha a partir de nuestra tabla de hecho o seteada a mano con la función DATE() y actual con NOW()
Date =
VAR MinYear = YEAR ( MIN ( TablaHecho[Fecha] ) )
VAR MaxYear = YEAR ( MAX ( TablaHecho[Fecha] ) )
RETURN
ADDCOLUMNS (
FILTER (
CALENDARAUTO( );
AND ( YEAR ( [Date] ) >= MinYear; YEAR ( [Date] ) <= MaxYear )
);
"Year Number"; YEAR ( [Date] );
"Month Name"; FORMAT ( [Date]; "mmmm" );
"Month Number"; MONTH ( [Date] );
"Day"; Day ( [Date] )
)
Para este tipo de casos yo soy de la frase "Mientras más atrás se resuelva, mejor". Si tenemos la posibilidad de construirlo en el origen de datos porque existe un data warehouse o tiene la posibilidad mejor. Sino intentaría siempre llevarlo por Power Query y de última instancia en DAX. No deberíamos construir modelos, ni aunque sean pequeños Power Bi de pequeñas necesidades, sin la tabla fecha. Ustedes, ¿usan tabla fecha?



