SQL Server Integration Services, SSIS de aquí en adelante, tiene la capacidad de cargar archivos Excel, pero en muchas ocasiones suele ser tedioso porque cualquier mínimo cambio en ese fichero Excel puede hacer fallar el paquete de SSIS, por eso normalmente la mejor opción es transformar dichos ficheros de formato Excel a CSV, ya que cargar ficheros de texto da muchos menos problemas que los ficheros Excel.
Convertir un solo fichero Excel a CSV se hace rápidamente de forma manual guardando el archivo como CSV desde Excel, el problema es cuando tienes que realizar este proceso con muchos ficheros Excel o necesitas que este cambio de formato se realice de forma automática.
Desde SolidQ queremos ayudarte a hacer este cambio de formato automáticamente usando PowerShell y como iterar por directorios para cargar varios ficheros Excel usando SSIS como herramienta principal para realizar todo el proceso.
1. Preparación del entorno
En nuestro ejemplo vamos a disponer de 2 de ficheros Excel, que se van a llamar Excel1.xlsx y Excel2.xlsx con 3 pestañas cada uno, estos ficheros contienen información de inventario sobre productos y están divididos por categorías en cada pestaña. En la siguiente imagen se muestra como es la estructura de los ficheros Excel:
Esta información va a ser guardada en la tabla Inventario que tiene la siguiente estructura:
CREATE TABLE [dbo].[Inventario]( [Item] [varchar](20) NULL, [Categoria] [varchar](20) NULL, [Stock] [int] NULL, [NombreFichero] [varchar](50) NULL, [InsertDate] [datetime] NULL )
Las columnas de NombreFichero e InsertDate son columnas que nos ayudaran para saber a qué fichero pertenece cada fila y a que día y hora se realizó la inserción en la tabla.
2. Conversión de ficheros Excel a CSV.
Lo primero que tenemos que hacer es crear 4 parámetros en nuestro paquete de SSIS:
- ExcelExtension: donde vamos a indicar la extensión de nuestro fichero Excel (xls o xlsx).
- RutaCSV: ruta donde se van a dejar los ficheros csv.
- RutaExcel: ruta donde se van a dejar los ficheros Excel.
- RutaPowerShell: ruta donde se va a dejar el script de PowerShell.
También vamos a necesitar 3 variables:
- commandPowerShell: donde vamos a guardar el comando para ejecutar el script de PowerShell.
- FileName: donde vamos a guardar el nombre del fichero una vez transformado a csv, para guardarnos el nombre en nuestra tabla de base de datos.
- FullFilePath: donde se va a guardar la ruta completa del fichero csv, esta variable se usará posteriormente cuando creemos el loop que iterará por los diferentes ficheros para ir cargándolos.
El siguiente paso es preparar la variable commandPowerShell parametrizada con las rutas y la extensión del fichero Excel, para ello tendremos que poner la siguiente “Expression” en la variable:
"-command \""+@[$Package::RutaPowerShell] + " -rutaExcel " + @[$Package::RutaExcel] + " -rutaCSV " + @[$Package::RutaCSV] + " -excelExt xlsx\""
Con los valores que yo tengo puestos en mis parámetros la variable quedaría de la siguiente manera:
Este es el código del archivo PowerShell que vamos a ejecutar:
<# #### Script para convertir archivos Excel a CSV #### Parametros: $rutaExcel: ruta origen donde se encuentran los ficheros excel $rutaCSV: ruta destino donde se van a dejar los ficheros csv $excelExt: extension del fichero excel (xls, xlsx) #> param ([string] $rutaExcel, [string] $rutaCSV, [string] $excelExt ) <# #### Funcion para convertir de excel a csv #### Parametros: $excelFileName: nombre del fichero excel $csvLoc: ruta destino de los ficheros csv $excelLoc: ruta origen donde se encuentras los ficheros excel $excelExtension: extension del fichero excel (xls, xlsx) #> Function ExportExcelToCSV ($excelFileName, $csvLoc, $excelLoc ,$excelExtension) { #guardamos la ruta del fichero excel $excelFile = $excelLoc + $excelFileName + $excelExtension $E = New-Object -ComObject Excel.Application $E.Visible = $false $E.DisplayAlerts = $false $wb = $E.Workbooks.Open($excelFile) #iteramos por cada sheet del fichero para convertirlo a csv foreach ($ws in $wb.Worksheets) { $n = $excelFileName + "_" + $ws.Name $ws.SaveAs($csvLoc + $n + ".csv", 6) } $E.Quit() } #mascara para coger todos los ficheros excel con la extension proporcionada $mascara = "*."+$excelExt #añadimos el punto a la extension $ext = "."+$excelExt #obtenemos todos los ficheros excel que cumplen con la mascara en la ruta especificada $ens = Get-ChildItem $rutaExcel -filter $mascara #iteramos por todos los ficheros excel encontrados y llamamos a la funcion para convertirlos a csv foreach($e in $ens) { ExportExcelToCSV -excelFileName $e.BaseName -csvLoc $rutaCSV -excelLoc $rutaExcel -excelExtension $ext }
Lo siguiente es realizar la llamada a nuestro script de PowerShell, para ello usaremos el componente “Execute Process Task”
Abrimos el componente y en “Process”, en la parte de Executable seleccionamos donde tenemos instalado PowerShell en nuestra máquina:
Luego en “Expressions” vamos a poner una expresión para parametrizar la parte de “Arguments” con la variable commandPowerShell que hemos configurado anteriormente.
Con esto ya podríamos realizar una pequeña prueba y ver si realmente funciona la parte de transformar los ficheros Excel a CSV.
Colocamos los ficheros Excel en la ruta:
Ejecutamos el componente:
Y una vez finalizada la ejecución, revisamos la ruta donde deberían estar los ficheros csv
Como se puede observar en la imagen se han creado 6 ficheros CSV, dichos ficheros tienen la nomenclatura de [NombreFicheroExcel]_[NombrePestañaExcel].csv
3. Carga de ficheros CSV en base de datos
Ahora toca la parte de cargar dichos ficheros csv en nuestra tabla de base de datos. Para ello vamos a usar el componente “Foreach Loop Container” para iterar por todos los directorios e ir cargando los ficheros csv.
Para configurarlo, en la sección de “Collection” vamos a seleccionar “Foreach File Enumerator” como Enumerator, después en ”Expressions” vamos a parametrizar la ruta de origen donde se encuentran nuestros ficheros csv usando nuestro parámetro del paquete “RutaCSV”, más abajo en el apartado de “Files:” pondremos la máscara “*.csv” para cargar todos los ficheros csv y en “Retrieve file name” seleccionamos “Fully qualified” para guardar la ruta completa del fichero, esta ruta completa del fichero la vamos a necesitar posteriormente para indicarle a nuestro origen de lectura donde se encuentra el fichero csv:
Luego en la sección de “Variable Mappings” vamos mapear nuestra variable FullFilePath, aquí es donde se va a guardar la ruta completa del fichero csv:
Con esto ya tendríamos configurado nuestro loop.
Ahora vamos a configurar las tareas que van a ir dentro de ese loop. Una de ellas será un “Script Task” para obtener el nombre del fichero csv a partir de la ruta completa del fichero y la otra será un “Data Flow” para cargar los ficheros csv en nuestra base de datos.
Vamos a empezar con el “Script Task”, para ello arrastramos el componente dentro del “Foreach Loop Container”. Hacemos doble click y en la sección de “Script” ponemos como ReadOnlyVariables la variable FullFilePath y como ReadWriteVariables la variable FileName:
Pulsamos sobre el botón de “Edit Script” e introducimos el siguiente código en el script dentro de la función Main:
public void Main() { //guardamos la ruta completa en una variable auxiliar string aux = Dts.Variables["User::FullFilePath"].Value.ToString(); //dividimos la ruta usando \ string[] split = aux.Split('\\'); //nos quedamos con la ultima parte de la ruta que es la que contiene el nombre del fichero string fileName = split[split.Length - 1]; //guardamos el nombre del fichero en nuestra varible Dts.Variables["User::FileName"].Value = fileName; Dts.TaskResult = (int)ScriptResults.Success; }
Seguimos con el “Data Flow”, lo arrastramos también dentro del componente loop y después del Script Task. Una vez dentro vamos a empezar con el origen, este origen va a ser un “Flat File Source”:
Lo abrimos y pulsamos sobre “New” para crear un nuevo Flat File Connection Manager.
La primera ventana del Flat File Connection Manager quedaría configurada de la siguiente manera:
En “File name” deberemos poner la ruta de alguno de los ficheros para hacer una primera configuración, más tarde esa ruta la parametrizaremos para que la coja automáticamente de lo que se va obteniendo de forma dinámica en el loop.
Luego pasaremos a la parte de “Advanced” para configurar las columnas que tendrá el fichero, vamos a tener 3 columnas, Item, Categoria y Stock.
Con esto ya quedaría configurado, el siguiente paso es la parametrización de la ruta del fichero, para esto en la parte inferior en “Connection Managers” seleccionamos la conexión InventarioCSV que acabamos de crear, botón derecho y “Propiedades”, se nos abrirán las propiedades en la parte derecha de la pantalla y en la propiedad “Expressions” pondremos la variable “FullFilePath” en el ConnectionString:
El siguiente paso será poner un “Conditional Split” para descartar las filas que pertenecen a la cabecera de los ficheros.
Como se puede ver en la siguiente imagen, cada pestaña de los ficheros Excel tenían una primera fila con la cabecera de la tabla, pero estas filas no las queremos guardar en nuestra tabla de base de datos:
Dentro de nuestro Conditional Split vamos a descartar dichas filas con la siguiente condición:
Item == "Item" || Item == ""
Todas las filas que tengan la columna Item vacía o que contengan la palabra “Item” no serán cargadas.
Llamamos “Detalle” a la salida por defecto y esta salida es la que vamos a conectar con nuestro siguiente componente que será un “Derived Column”:
Este Derived Column lo vamos a usar para añadir las columnas que contendrán el nombre del fichero y la fecha de inserción, la configuración sería la siguiente:
Y por último ya solo quedaría nuestro destino, que será un componente “OLE DB Destination”
Lo abrimos y pulsamos sobre “New” para crear el connection manager con la conexión con nuestra base de datos.
Una vez configurada esta conexión seleccionaremos la tabla donde queremos guardar los datos, en nuestro ejemplo será la tabla Inventario que hemos creado anteriormente:
Y por último en la parte de “Mappings” mapearemos las columnas que llegan por nuestro flujo con las columnas de la tabla:
Esta sería una imagen final de como quedaría nuestro Data Flow:
Y esto es una imagen final de como quedaría nuestro Control Flow:
Al principio de todo hemos añadido un “Execute SQL Task” que contiene un truncado de la tabla Inventario.
Ahora ya lo tenemos todo listo para ejecutar nuestro paquete y revisar el resultado:
Como se puede observar se han creado los ficheros CSV en nuestro directorio:
Y posteriormente se han cargado estos ficheros en nuestra tabla:
Ahora ya sabéis qué importante es cambiar de formato Excel a CSV para cuestiones con SSIS.
Si queréis más información sobre SSIS, aquí te dejamos un artículo donde uno de nuestro expertos trata en mayor profundidad este tema.
¡Esperamos que os sirva de ayuda y si queréis seguir recibiendo consejos y trucos sobre este tema y relacionados podéis suscribiros a nuestra newsletter! 🙂
11 comments
Muy buen tutorial solo una consulta donde esta el archivo ConvertToCSV.ps1 o donde lo puedo obtener. Gracias de antemano
Gracias por el comentario Alex.
Escribo el código por aquí y en unos días intentaremos actualizar el post para que lo incluya.
ConvertToCSV.ps1:
param ([string] $rutaExcel, [string] $rutaCSV, [string] $excelExt )
Function ExportExcelToCSV ($excelFileName, $csvLoc, $excelLoc ,$excelExtension)
{
#guardamos la ruta del fichero excel
$excelFile = $excelLoc + $excelFileName + $excelExtension
$E = New-Object -ComObject Excel.Application
$E.Visible = $false
$E.DisplayAlerts = $false
$wb = $E.Workbooks.Open($excelFile)
#iteramos por cada sheet del fichero para convertirlo a csv
foreach ($ws in $wb.Worksheets)
{
$n = $excelFileName + “_” + $ws.Name
$ws.SaveAs($csvLoc + $n + “.csv”, 6)
}
$E.Quit()
}
#mascara para coger todos los ficheros excel con la extension proporcionada
$mascara = “*.”+$excelExt
#añadimos el punto a la extension
$ext = “.”+$excelExt
#obtenemos todos los ficheros excel que cumplen con la mascara en la ruta especificada
$ens = Get-ChildItem $rutaExcel -filter $mascara
#iteramos por todos los ficheros excel encontrados y llamamos a la funcion para convertirlos a csv
foreach($e in $ens)
{
ExportExcelToCSV -excelFileName $e.BaseName -csvLoc $rutaCSV -excelLoc $rutaExcel -excelExtension $ext
}
Hola excelente articulo, gracias, solo una duda ´powershell por politica de seguridad no permite la ejecucion de codigo no firmado, deberias advertir que no funciona si no se ejecuta un par de comandos previamente
gracias
Gracias por el feedback, lo añadiré.
Un saludo.
Carmina Bernabeu
Hola, que comandos se necesitan previamente, ya que sale el siguiente error:
SSIS package “C:\Users\USER\source\repos\ISP test\ISP test\Package.dtsx” starting.
Error: 0xC0029151 at Execute Process Task, Execute Process Task: In Executing “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe” “-command “C:\Users\USER\Documents\Post\ConvertToCSV.ps1 -rutaExcel C:\Users\USER\Documents\Post\ExcelFiles\ -rutaCSV C:\Users\USER\Documents\Post\CSVFiles\ -excelExt xlsx”” at “”, The process exit code was “1” while the expected was “0”.
Task failed: Execute Process Task
Warning: 0x80019002 at Package: SSIS Warning Code DTS_W_MAXIMUMERRORCOUNTREACHED. The Execution method succeeded, but the number of errors raised (1) reached the maximum allowed (1); resulting in failure. This occurs when the number of errors reaches the number specified in MaximumErrorCount. Change the MaximumErrorCount or fix the errors.
SSIS package “C:\Users\USER\source\repos\ISP test\ISP test\Package.dtsx” finished: Failure.
The program ‘[7520] DtsDebugHost.exe: DTS’ has exited with code 0 (0x0).
Hola Jose,
Te contesto por correo y lo vemos con más detalle.
Un saludo.
Hola muy buenos dias Carmina, muchas gracias por la info,
no tienes un video sobre esto ??, estoy tratando de aplicarlo pero me resulta mas complicado así,
si lo tienes te lo agradeceria
estoy pegado en la parte del Execute Process Task en “Expressions” vamos a poner una expresión para parametrizar la parte de “Arguments” con la variable commandPowerShell que hemos configurado anteriormente,
necesito aplicar esto ya que me envian un excel todos los dias y quiero automatizar la conversión y subida a sql server,
como puedo contactarte?
saludos
Hola Elías,
No tengo ningún video, te voy a enviar el paquete de ssis por correo a ver si eso te ayudar.
Ya me comentas si necesitas algo más.
Un saludo,
Carmina Bernabeu
Hola que tal. A mi también me podrías enviar el paquete SSIS por correo
Muchas gracias y que buen blog
Hola Daniel, ya no tengo el paquete, pero ¿Te puedo ayudar en algo?
[Execute Process Task] Error: In Executing “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe” “-command “Z:\ConvertToCSV.ps1 -rutaExcel Z:\ -rutaCSV Z:\ -excelExt xlsx”” at “”, The process exit code was “1” while the expected was “0”.
Hi, I am getting this error ,Can you provide some insights?