Generar secuencias de fecha con CTE Recursivo

En ocasiones necesitamos generar secuencias numéricas o de fechas para diferentes propósitos. En este caso vamos a ver un script para generar una secuencia de fechas y aprovechar diferentes funciones del sistema para llenar una tabla de dimensión de tiempo de un datamart.

 

Para ello vamos a usar Common Table Expresions o CTE recursivo (). La recursividad nos permite iterar para generar la secuencia. Vamos a mostrar el script () y a explicar de qué se trata:

 

–define límites de inicio y fin

DECLARE @FechaDesde date, @FechaHasta date

SELECT @FechaDesde=‘2009-01-01’, @FechaHasta=‘2015-12-31’;

 

–Loop para generar las fechas

WITH DateSequence(Fecha) AS

(    SELECT @FechaDesde as Fecha

        UNION ALL                                           –>(1)

    SELECT DATEADD(DAY, 1, Fecha)

    FROM DateSequence

    WHERE Fecha < @FechaHasta

)

–select final para obtener la secuencia

SELECT                                                      –>(2)

  Fecha,

  YEAR(Fecha) AS AñoNum,

  MONTH(Fecha) AS MesNum,

  YEAR(Fecha) AS AñoChar,

  RIGHT(‘0’+CAST(MONTH(Fecha) as varchar(2)),2) AS MesChar, –>(3)

  DATENAME(DW,Fecha) AS NombreDia,                          –>(4)

  DATENAME(DAYOFYEAR,Fecha) AS DiaDelAño,

  DATENAME(MONTH,Fecha) AS NombreMes,

  DATENAME(WEEK,Fecha) AS SemanaAño,

  DATENAME(QQ,Fecha) AS Trimestre,

  Case                                                      –>(5)

   WHEN MONTH(Fecha)>=1 AND MONTH(Fecha)<=6 THEN 1

   WHEN MONTH(Fecha)>=7 AND MONTH(Fecha)<=12 THEN 2

  end Semestre

FROM DateSequence OPTION (MAXRECURSION 3000)                –>(6)

GO

 

Como vemos, primero tenemos la definición de variables necesarias para el proceso. Luego tenemos la definición del CTE que comienza con la cláusula WITH y por último el query que utiliza los datos generados en el CTE y aprovecha diferentes funciones del sistema para completar información de la dimensión tiempo. A la derecha hay varios números que indican algunos puntos que deseo resaltar:

 

(1)    Este es el query recursivo dentro del CTE. Hay dos consultas unidas a través de la cláusula UNION ALL. En el primer SELECT se obtiene el valor de @FechaDesde con el alias Fecha y en el segundo SELECT se le añade un día al valor de Fecha utilizando la función DATEADD (), con una condición WHERE que establece como limite el valor de @FechaHasta. Esto va a hacer que obtengamos una iteración desde la fecha de inicio hasta la fecha de fin, avanzando día por día. Cambiando la función DATEADD puedo variar el intervalo a mes o año, según se requiera.

 

(2)    En este caso estamos ejecutando una consulta con SELECT, la cual aprovecha los datos generados en el CTE. Ojo que el CTE solo bota el valor Fecha, el cual se aprovecha para obtener más información.

 

(3)    Se ha diseñado la consulta para obtener el dato de mes de forma numérica y carácter. En esta línea se formatea el dato carácter añadiendo un 0 a la izquierda para uniformizar el dato, pero teniendo la precaución de que todos los meses tengan una longitud de dos caracteres. Así, octubre, noviembre y diciembre no llevan 0 a la izquierda.

 

(4)    Con la función DATENAME podemos obtener varias formas de representar el valor de Fecha. Tenerlo ya almacenado en la tabla de dimensión nos permite utilizarlo directamente en un cubo o en una consulta sin necesidad de estar aplicando constantemente funciones. Recuerden que el objetivo de un datamart es estar optimizado para la velocidad de las consultas.

 

(5)    No encontré una forma de obtener el semestre utilizando DATENAME así que trabajé una sentencia Case utilizando como condición el mes de la fecha.

 

(6)    MAXRECURSION () es necesario en este query. Por defecto los CTE recursivos solo iteran 100 veces y aquí obtenemos más de 2000 fechas. Prueben quitando esta cláusula y verán como sale un error.

 

El resultado de ejecutar el script es el siguiente (se muestran solo las primeras líneas):

 

NOTA: Si los nombres de los días o meses aparecen en ingles, ejecutar el siguiente comando: SET LANGUAGE SPANISH

 

clip_image002[4]

 

Si deseamos utilizar la secuencia para insertar los datos en una tabla, solo ha que incluir una sentencia INSERT en la consulta que utiliza el CTE. Para hacer la prueba, primero crearemos una tabla:

 

CREATE TABLE dbo.dTiempo(

IdFecha smallint IDENTITY(1,1) not null, –Columna con identidad

Fecha date not null,                     –fecha DD/MM/AAA

AñoNum int not null,                     –Año en formato numérico

MesNum tinyint not null,                 –Mes en formato numérico

AñoChar char(4) not null,                –Año en formato caracter

MesChar char(2) not null,                –Mes en formato caracter

NombreDia varchar(10) not null,          –Nombre del día en letras

DiaDelAño smallint not null,             –Numero de día en todo el año

NombreMes varchar(10) not null,          –Nombre del mes en letras

SemanaDelAño tinyint not null,           –Numero de semana del año

Trimestre tinyint not null,              –Numero de trimestre

Semestre tinyint not null)               –Numero de semestre

GO

 

ALTER TABLE dbo.dTiempo

ADD CONSTRAINT PK_dTiempo

PRIMARY KEY (IdFecha)

GO

 

Una vez creada la tabla ejecutamos el mismo script con la sentencia INSERT en el query:

 

NOTA: cuidado al ejecutar repetidamente este script por que puede crear registros duplicados

 

–define límites de inicio y fin

DECLARE @FechaDesde date, @FechaHasta date

SELECT @FechaDesde=‘2009-01-01’, @FechaHasta=‘2015-12-31’;

 

–Loop para generar las fechas

WITH DateSequence(Fecha) AS

(    SELECT @FechaDesde as Fecha

        UNION ALL                                          

    SELECT DATEADD(DAY, 1, Fecha)

    FROM DateSequence

    WHERE Fecha < @FechaHasta

)

–select final para insertar los registros

INSERT INTO dbo.dTiempo                           –>Sentencia Insert

SELECT                                                     

  Fecha,

  YEAR(Fecha) AS AñoNum,

  MONTH(Fecha) AS MesNum,

  YEAR(Fecha) AS AñoChar,

  RIGHT(‘0’+CAST(MONTH(Fecha) as varchar(2)),2) AS MesChar,

  DATENAME(DW,Fecha) AS NombreDia,                         

  DATENAME(DAYOFYEAR,Fecha) AS DiaDelAño,

  DATENAME(MONTH,Fecha) AS NombreMes,

  DATENAME(WEEK,Fecha) AS SemanaAño,

  DATENAME(QQ,Fecha) AS Trimestre,

  Case                                                     

   WHEN MONTH(Fecha)>=1 AND MONTH(Fecha)<=6 THEN 1

   WHEN MONTH(Fecha)>=7 AND MONTH(Fecha)<=12 THEN 2

  end Semestre

FROM DateSequence OPTION (MAXRECURSION 3000)               

GO

 

Los animo a que modifiquen el script para obtener otros datos de fecha o también obtener secuencia numéricas.

Deja un comentario

Tu dirección de correo electrónico no será publicada.