Tengo un algoritmo que necesito ejecutar en cada fila de una tabla con 800K filas y 38 columnas. El algoritmo se implementa en VBA y hace un montón de matemáticas usando valores de algunas columnas para manipular otras columnas.
Actualmente estoy usando Excel (ADO) para consultar SQL y usar VBA con cursores del lado del cliente para aplicar el algoritmo por bucle a través de cada fila. Funciona pero tarda 7 horas en ejecutarse.
El código VBA es lo suficientemente complejo como para que sea mucho trabajo recodificarlo en T-SQL.
He leído sobre la integración CLR y las UDF como posibles rutas. También pensé en poner el código VBA en una tarea de script SSIS para acercarme a la base de datos, pero estoy seguro de que existe una metodología experta para este tipo de problema de rendimiento.
Lo ideal sería poder ejecutar el algoritmo contra tantas filas (¿todas?) Como sea posible de forma paralela basada en conjuntos.
Cualquier ayuda se basa en gran medida en cómo obtener el mejor rendimiento con este tipo de problema.
--Editar
Gracias por los comentarios, estoy usando MS SQL 2014 Enterprise, aquí hay más detalles:
El algoritmo encuentra patrones característicos en datos de series de tiempo. Las funciones dentro del algoritmo realizan suavizado polinómico, ventanas y encuentran regiones de interés basadas en criterios de entrada, devolviendo una docena de valores y algunos resultados booleanos.
Mi pregunta es más sobre la metodología que el algoritmo real: si quiero lograr el cálculo paralelo en muchas filas a la vez, ¿cuáles son mis opciones?
Veo que se recomienda volver a codificar en T-SQL, que es mucho trabajo pero posible, sin embargo, el desarrollador del algoritmo trabaja en VBA y cambia con frecuencia, por lo que necesitaría mantener la sincronización con la versión de T-SQL y volver a validar cada cambio.
¿Es T-SQL la única forma de implementar funciones basadas en conjuntos?
fuente
N
lotes y ejecutarN
instancias de su algoritmo enN
procesadores / computadoras separadas. Por otro lado, ¿cuál es su principal cuello de botella: transferir los datos de SQL Server a Excel o los cálculos reales? Si cambia la función VBA para devolver algún resultado ficticio de inmediato, ¿cuánto tiempo llevará todo el proceso? Si todavía lleva horas, entonces el cuello de botella está en la transferencia de datos. Si lleva segundos, entonces necesita optimizar el código VBA que hace los cálculos.SELECT AVG([AD_Sensor_Data]) OVER (ORDER BY [RowID] ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING) as 'AD_Sensor_Data' FROM [AD_Points] WHERE [FileID] = @FileID ORDER BY [RowID] ASC
en Management Studio, esta función que se llama para cada una de las filas tarda 50 ms(FileID, RowID)
.Respuestas:
Con respecto a la metodología, creo que estás ladrando el árbol b incorrecto ;-).
Lo que sabemos:
Primero, consolidemos y revisemos lo que sabemos sobre la situación:
Hay un procedimiento almacenado que se llama para cada fila:
La definición (al menos en parte) es:
Lo que podemos suponer:
Luego, podemos ver todos estos puntos de datos juntos para ver si podemos sintetizar detalles adicionales que nos ayudarán a encontrar uno o más cuellos de botella, y señalar una solución, o al menos descartar algunas posibles soluciones.
La dirección actual de pensamiento en los comentarios es que el problema principal es la transferencia de datos entre SQL Server y Excel. ¿Es realmente el caso? Si se llama al Procedimiento almacenado para cada una de las 800,000 filas y toma 50 ms por cada llamada (es decir, por cada fila), eso suma 40,000 segundos (no ms). Y eso equivale a 666 minutos (hhmm ;-), o poco más de 11 horas. Sin embargo, se dijo que todo el proceso demoraba solo 7 horas en ejecutarse. Ya hemos pasado 4 horas sobre el tiempo total, e incluso hemos agregado a tiempo para hacer los cálculos o guardar los resultados en SQL Server. Entonces algo no está bien aquí.
Mirando la definición del Procedimiento almacenado, solo hay un parámetro de entrada para
@FileID
; No hay ningún filtro activado@RowID
. Entonces sospecho que uno de los siguientes dos escenarios está sucediendo:@FileID
, que parece abarcar aproximadamente 4000 filas. Si las 4000 filas indicadas devueltas son una cantidad bastante consistente, entonces solo hay 200 de esas agrupaciones en las 800,000 filas. Y 200 ejecuciones de 50 ms cada una equivalen a solo 10 segundos de esas 7 horas.@FileID
se pasa una nueva no tomaría un poco más de tiempo para atraer nuevas filas al Buffer Pool, pero luego las siguientes 3999 ejecuciones generalmente regresarían más rápido debido a que ya en caché, ¿verdad?Creo que centrarse en este procedimiento almacenado de "filtro", o cualquier transferencia de datos desde SQL Server a Excel, es una pista falsa .
Por el momento, creo que los indicadores más relevantes de rendimiento mediocre son:
Sospecho que:
UPDATE
extractos, que son 800,000 transacciones separadas.Mi recomendación (basada en la información disponible actualmente):
Su mayor área de mejora sería actualizar varias filas a la vez (es decir, en una transacción). Debe actualizar su proceso para que funcione en términos de cada uno en
FileID
lugar de cada unoRowID
. Entonces:FileID
en una matrizFileID
se han calculado ):RowID
Si su índice agrupado aún no está definido,
(FileID, RowID)
entonces debería considerarlo (como sugirió @MikaelEriksson en un comentario sobre la Pregunta). No ayudará a estas ACTUALIZACIONES de singleton, pero al menos mejoraría ligeramente las operaciones agregadas, como lo que está haciendo en ese procedimiento almacenado de "filtro", ya que todas se basan en ellasFileID
.Debería considerar mover la lógica a un lenguaje compilado. Sugeriría crear una aplicación .NET WinForms o incluso una aplicación de consola. Prefiero la aplicación de consola, ya que es fácil de programar a través del Agente SQL o las tareas programadas de Windows. No debería importar si se hace en VB.NET o C #. VB.NET puede ser más adecuado para su desarrollador, pero seguirá habiendo cierta curva de aprendizaje.
No veo ninguna razón en este momento para pasar a SQLCLR. Si el algoritmo cambia con frecuencia, sería molesto tener que volver a implementar la Asamblea todo el tiempo. Reconstruir una aplicación de consola y hacer que el .exe se coloque en la carpeta compartida adecuada en la red de modo que simplemente ejecute el mismo programa y siempre esté actualizado, debería ser bastante fácil de hacer.
No creo que mover el procesamiento completamente a T-SQL ayudaría si el problema es lo que sospecho y solo está haciendo una ACTUALIZACIÓN a la vez.
Si el procesamiento se traslada a .NET, puede utilizar los Parámetros con valor de tabla (TVP) de modo que pase la matriz a un Procedimiento almacenado que llame a un
UPDATE
que se UNE a la variable de tabla de TVP y, por lo tanto, sea una sola transacción . El TVP debería ser más rápido que hacer 4000INSERT
s agrupados en una sola transacción. Pero la ganancia proveniente del uso de TVP durante 4000INSERT
s en 1 transacción probablemente no será tan significativa como la mejora observada al pasar de 800,000 transacciones separadas a solo 200 transacciones de 4000 filas cada una.La opción TVP no está disponible de forma nativa para el lado de VBA, pero a alguien se le ocurrió una solución que podría valer la pena probar:
¿Cómo mejoro el rendimiento de la base de datos cuando paso de VBA a SQL Server 2008 R2?
SI el proceso de filtro solo se usa
FileID
en laWHERE
cláusula, y si realmente se llama a ese proceso por cada fila, entonces puede ahorrar algo de tiempo de procesamiento almacenando en caché los resultados de la primera ejecución y usándolos para el resto de las filas por esoFileID
, ¿Derecha?Una vez que el procesamiento realizado por FileID , entonces podemos empezar a hablar de procesamiento en paralelo. Pero eso podría no ser necesario en ese momento :). Dado que se trata de 3 partes no ideales bastante importantes: transacciones de Excel, VBA y 800k, cualquier conversación sobre SSIS o paralelogramos, o quién sabe qué, es un tipo de optimización prematura / carro antes del caballo . Si podemos reducir este proceso de 7 horas a 10 minutos o menos, ¿seguiría pensando en formas adicionales de acelerarlo? ¿Hay un tiempo de finalización objetivo que tenga en mente? Tenga en cuenta que una vez que el procesamiento se realiza en un ID de archivo base, si tuviera una aplicación de consola VB.NET (es decir, línea de comandos .EXE), no habría nada que le impidiera ejecutar algunos de esos ID de archivo a la vez :), ya sea a través del paso CmdExec del Agente SQL o Tareas programadas de Windows, etc.
Y, siempre puede adoptar un enfoque "por fases" y hacer algunas mejoras a la vez. Como comenzar con las actualizaciones por
FileID
y, por lo tanto, usar una transacción para ese grupo. Luego, vea si puede hacer que el TVP funcione. Luego, vea cómo tomar ese código y moverlo a VB.NET (y los TVP funcionan en .NET, por lo que se portará bien).Lo que no sabemos que aún podría ayudar:
ACTUALIZACIÓN 1:
** Parece haber cierta confusión acerca de qué VBA (Visual Basic para aplicaciones) y qué se puede hacer con él, así que esto es solo para asegurarse de que todos estamos en la misma página web:
ACTUALIZACIÓN 2:
Un punto más a considerar: ¿Cómo se manejan las conexiones? ¿El código VBA abre y cierra la conexión por cada operación, o abre la conexión al comienzo del proceso y la cierra al final del proceso (es decir, 7 horas después)? Incluso con la agrupación de conexiones (que, de forma predeterminada, debería estar habilitada para ADO), todavía debería haber un gran impacto entre abrir y cerrar una vez en lugar de abrir y cerrar 800.200 o 1.600.000 veces. Esos valores se basan en al menos 800,000 ACTUALIZACIONES más 200 u 800k EXEC (dependiendo de con qué frecuencia se ejecute el procedimiento almacenado del filtro).
Este problema de demasiadas conexiones se mitiga automáticamente mediante la recomendación que describí anteriormente. Al crear una transacción y hacer todas las ACTUALIZACIONES dentro de esa transacción, mantendrá esa conexión abierta y la reutilizará para cada una
UPDATE
. Si la conexión se mantiene abierta o no desde la llamada inicial para obtener las 4000 filas según lo especificadoFileID
, o se cierra después de esa operación "get" y se abre nuevamente para las ACTUALIZACIONES, es mucho menos impactante ya que ahora estamos hablando de una diferencia de 200 o 400 conexiones totales en todo el proceso.ACTUALIZACIÓN 3:
Hice algunas pruebas rápidas. Tenga en cuenta que esta es una prueba a pequeña escala, y no exactamente la misma operación (INSERT puro vs ACTUALIZACIÓN EXEC +). Sin embargo, las diferencias en el tiempo relacionadas con la forma en que se manejan las conexiones y las transacciones siguen siendo relevantes, por lo tanto, la información puede extrapolarse para tener un impacto relativamente similar aquí.
Parámetros de prueba:
Mesa:
Operación:
TRUNCATE TABLE dbo.ManyInserts;
(dada la naturaleza de esta prueba, hacer el FREEPROCCACHE, FREESYSTEMCACHE y DROPCLEANBUFFERS no parecía agregar mucho valor).Resultados:
Como puede ver, incluso si la conexión ADO a la base de datos ya se está compartiendo en todas las operaciones, se garantiza que agruparlos en lotes mediante una transacción explícita (el objeto ADO debería ser capaz de manejar esto) significativamente (es decir, más del doble de mejora) Reducir el tiempo total del proceso.
fuente
En mi humilde opinión y trabajando desde el supuesto de que no es posible volver a codificar el sub VBA en SQL, ¿ha considerado permitir que el script VBA termine de evaluar en el archivo Excel y luego escribir los resultados en el servidor SQL a través de SSIS?
Puede hacer que el sub VBA comience y termine volteando un indicador en un objeto de sistema de archivos o en el servidor (si ya ha configurado la conexión para volver a escribir en el servidor) y luego use una expresión SSIS para verificar este indicador para
disable
propiedad de una tarea determinada dentro de su solución SSIS (para que el proceso de importación espere hasta que el sub VBA se complete si le preocupa que sobrepase su programación).Además, puede hacer que el script de VBA se inicie mediante programación (un poco inestable, pero he usado la
workbook_open()
propiedad para activar tareas de "disparar y olvidar" de esta naturaleza en el pasado).Si el tiempo de evaluación de la secuencia de comandos VB comienza a convertirse en un problema, puede ver si su desarrollador de VB está dispuesto y es capaz de transferir su código a una tarea de secuencia de comandos VB dentro de la solución SSIS; en mi experiencia, la aplicación de Excel genera una gran carga cuando trabajando con datos en este volumen.
fuente