No es algo que haga habitualmente, pero en casi todos los clientes termino codificando algún script (en VB.NET lo siento por los de c#) y como hace mucho que no escribo nada, vamos a volver a la carga con este tema.
Dentro de los paquetes de SSIS hay varios sitios donde podemos incluir tareas de script, si bien el contenido va a ser código generado por nosotros, el objetivo y la forma de tratarlos es distinta, pero principalmente podemos distinguir entre scripts dentro de un flujo de datos y las scripts que están fuera de un flujo de datos.
Los que están fuera de un flujo de datos son seguramente más fáciles de entender ya que no deja de ser un “trozo” de código que va a ejecutar una tarea concreta dentro de nuestro flujo de control y que podemos considerar como una mini aplicación dentro de nuestro paquete. Además esas tareas aceptan depuración y podemos ver qué ocurre mientras se ejecuta de una manera sencilla tal y como están acostumbrados los buenos desarrolladores de código. Sin embargo los que están dentro de los flujos de control tienen sus propias singularidades que son las que quiero contar:
- De primeras, no admiten depuración, una vez que se ejecuta el flujo de datos donde están contenidos, no hay manera de hacer una depuración de código, así que la única manera de ver que contiene las variables que hemos definido dentro es a fuerza de MsgBox (que comentaremos una vez hayamos asegurado que el código funciona como esperábamos)
- Admiten tanto código VB.NET como C#, pero una vez se ha elegido uno de los dos lenguajes, ya no puedes volver atrás.
- Puede actuar de tres maneras distintas, como un origen de datos, como una transformación o como un destino:
- Si es un origen de datos, en nuestro componente tendremos que definir un conjunto de columnas en un flujo de salida y por código iremos generando las filas que van a alimentar el resto del flujo de datos.
- Si es un destino de datos, solo tenemos que definir la entrada a partir del flujo de datos que vaya a recibir.
- Si es una transformación de datos, una vez definidos los campos de la entrada en función del flujo que recibe, hay que definir una salida. La definición de esa salida es clave en cómo se va a comportar nuestro componente.
- Si directamente enlazamos la salida con la entrada, el objeto se comportará de forma síncrona y según entren las filas, serán tratadas por el script y se lanzarán al buffer de salida.
- Sin embargo, si eliminamos la referencia del buffer de entrada (como se ve en la pantalla de abajo), será el script el encargado de recoger las filas de entrada, tratarlas y definir cuando hay una fila lista para ser lanzada al buffer de salida. Se genera por tanto una tarea asíncrona.
En estos casos tenemos que definir que columnas van a componer el buffer de salida, ir avanzando fila a fila por código y generar la fila en el buffer de salida.
En este ejemplo vamos a contar el número de filas que pertenecen a una misma organización y tiempo. La transformación recibirá un flujo de datos ordenado por fecha y organización e incluiremos el siguiente código:
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
'
' Add your code here
'
Dim iSubtotal As Integer
Dim iPrevTime As Integer
Dim iPrevOrganization As Integer
'Inicializamos las variables con el valor de la primera fila del buffer de entrada
iSubtotal = 1
iPrevOrganization = Row.OrganizationKey
iPrevTime = Row.TimeKey
'Mientras haya filas en el buffer de entrada avanzamos
While Row.NextRow
If iPrevOrganization = Row.OrganizationKey And iPrevTime = Row.TimeKey Then
'Si la fila pertenece al mismo tiempo y organización contamos la fila y avanzamos el buffer
'hasta la siguiente fila
iSubtotal = iSubtotal + 1
Row.NextRow()
Else
'Si hemos cambiado de tiempo y organización, nos quedamos con los nuevos valores para
'chequear el cambio en los datos
iPrevOrganization = Row.OrganizationKey
iPrevTime = Row.TimeKey
'Creamos la fila con la información del número de filas por tiempo y organización
SalidaAgrupadaBuffer.AddRow()
SalidaAgrupadaBuffer.TimeKey = Row.TimeKey
SalidaAgrupadaBuffer.OrganizationKey = Row.OrganizationKey
SalidaAgrupadaBuffer.NumeroFilas = iSubtotal
End If
End While
'Cerramos el buffer de salida para notificarlo al flujo de datos
SalidaAgrupadaBuffer.SetEndOfRowset()
End Sub
En los visores de datos, vemos como recibimos un conjunto ordenado de filas donde se repiten los datos de tiempo y organización y en el visor de datos de la salida vemos que solo tenemos un registro por combinación de clave de tiempo y organización con el número de registros.
- Sin embargo, si eliminamos la referencia del buffer de entrada (como se ve en la pantalla de abajo), será el script el encargado de recoger las filas de entrada, tratarlas y definir cuando hay una fila lista para ser lanzada al buffer de salida. Se genera por tanto una tarea asíncrona.
- Si directamente enlazamos la salida con la entrada, el objeto se comportará de forma síncrona y según entren las filas, serán tratadas por el script y se lanzarán al buffer de salida.