En ocasiones nos encontramos con fuentes de datos que modelan rangos de tiempo empleando una columna de fecha de inicio y otra de fecha final. Por ejemplo en una tabla de precios puede almacenar el precio histórico de un producto como:

CREATE TABLE Precios(

     CodigoProducto            INT NOT NULL

    , FechaInicialPrecio            SMALLDATETIME NOT NULL

    , FechaFinalPrecio            SMALLDATETIME NULL    — Permite Nulos

    , PrecioVenta                NUMERIC(19,2)    NOT NULL

    , CONSTRAINT PK_Precios

        PRIMARY KEY(CodigoProducto, FechaFinal)

);

 

Esta tabla tiene una fila para cierto rango de fechas en las que rige el precio, y no una para cada día. Es posible modelar una estrella que capture esta información usando el mismo patrón de modelamiento:

CREATE TABLE FactPrecios(

     ProductoID        INT NOT NULL

    , FechaInicioID        INT NOT NULL

    , FechaFinalID        INT NOT NULL

    , PrecioVenta        NUMERIC(19,2)    NOT NULL

);

 

Pero la verdad este modelo no es muy usable, y la experiencia del usuario no es tan buena, ya que exige de dos dimensiones de Cubo (Rol Playing Dimension), por lo que en mi opinión es preferible modelar as:

CREATE TABLE FactPrecios(

     ProductoID        INT NOT NULL

    , FechaID            INT NOT NULL

    , PrecioVenta        NUMERIC(19,2)    NOT NULL

);

Esta tabla tiene un precio para cada día y es más fácil de manipular por parte del usuario y por los cálculos que referencien en precio.

El problema entonces es cómo construir un paquete de integration services de SQL que pase los datos del primer modelo al último. Para esta labor una buena opción es utilizar una tarea de script que se encargue de explotar todas las fechas entre FechaInicioID y FechaFinalId. Como esta tarea requiere pocas filas, puede usarse de forma asincrónica.

Para programarla:

  1. Se definen a nivel de paquete de SSIS dos variables (FechaInicio y FechaFinal) que tienen el rango de fechas de la tabla de hechos que se va a procesar. Estas variables son parámetros del paquete que pueden configurarse externamente.
  2. En el DataFlow se arrastra y se deja caer una tarea de script.
  3. Se selecciona una transformación.

  4. Se conecta la tarea de con la fuente de los datos.
  5. Se hace doble clic sobre la tarea de script y se configuran como entrada las columnas apropiadas. En nuestro ejemplo CodigoProducto, FechaInicialPrecio, FechaFinalPrecio y PrecioVenta. En la sección de Script se configuraran como variables de ReadOnly las variables de FechaInicio y FechaFinal.
  6. Se selecciona la opción de Input and Outputs, y se elimina el Output0.
  7. Se agregan un nuevo Output, manteniendo la opción default de SincronousInputId en -1.
  8. Se agregan las columnas de Output, asegurando que el tipo sea el adecuado.

  9. Luego volviendo a la opción de Script, oprimir el botón de Script para agregar el código.
  10. Escribir una sobrecarga de la función Input0_ProcessInput de la siguiente forma:

public override void Input0_ProcessInput(Input0Buffer Buffer)

{

while (Buffer.NextRow()) /* Para cada fila, procese los datos */

{

Input0_ProcessInputRow(Buffer);

}

if (Buffer.EndOfRowset()) /* Si ya termino los datos, cierre el rowset Precios */

{

PreciosBuffer.SetEndOfRowset();

}

}

  1. La función anterior sobre-escribe la función cuando recibe un Input0Buffer, y debe distinguirse de la que se agrega automáticamente que en lugar de procesar el buffer completo, procesa cada fila.

     

  2. Agregar en la función Input0_ProcessInputRow (la que fue agregada automáticamente) el siguiente código:

public override void Input0_ProcessInputRow(Input0Buffer Row)

{

DateTime Fecha_Inicio;

DateTime Fecha_Final;

Fecha_Inicio = (Variables.FechaInicio > Row. FechaInicialPrecio) ? Variables.FechaInicio : Row. FechaInicialPrecio;

Fecha_Final = ((Variables.FechaFinal < Row. FechaFinalPrecio)

|| Row. FechaFinalPrecio _IsNull) ? Variables. FechaFinal: Row. FechaFinalPrecio;

for (DateTime Fecha = Fecha_Inicio; Fecha <= Fecha_Final; Fecha =Fecha.AddDays(1))

{

PreciosBuffer.AddRow();

PreciosBuffer.CodigoProducto = Row.TipoCambioOrigen;

PreciosBuffer.Fecha = Fecha;

PreciosBuffer.Precio = Row.TipoCambio;

}

}

Y listo!!!.

 

Últimas entradas de Javier Loria (ver todo)