Eu tenho um algoritmo que eu preciso executar em todas as linhas de uma tabela com 800K linhas e 38 colunas. O algoritmo é implementado no VBA e faz um monte de cálculos usando valores de algumas colunas para manipular outras colunas.
Atualmente, estou usando o Excel (ADO) para consultar o SQL e usar o VBA com cursores do lado do cliente para aplicar o algoritmo por loop em todas as linhas. Funciona, mas leva 7 horas para ser executado.
O código VBA é complexo o suficiente para que seria muito trabalhoso recodificá-lo em T-SQL.
Eu li sobre integração CLR e UDFs como possíveis rotas. Também pensei em colocar o código VBA em uma tarefa de script SSIS para aproximar-me do banco de dados, mas tenho certeza de que existe uma metodologia especializada para esse tipo de problema de desempenho.
Idealmente, eu seria capaz de executar o algoritmo contra o maior número possível de linhas (todas?) De uma maneira baseada em conjunto paralelo.
Qualquer ajuda baseada em como obter o melhor desempenho com esse tipo de problema.
--Editar
Obrigado pelos comentários, estou usando o MS SQL 2014 Enterprise, aqui estão mais alguns detalhes:
O algoritmo encontra padrões característicos nos dados de séries temporais. As funções no algoritmo executam suavização polinomial, janelas e localizam regiões de interesse com base nos critérios de entrada, retornando uma dúzia de valores e alguns resultados booleanos.
Minha pergunta é mais sobre metodologia do que o algoritmo real: se eu quiser obter computação paralela em várias linhas ao mesmo tempo, quais são minhas opções.
Vejo que é recomendado re-codificar no T-SQL, o que é muito trabalhoso, mas possível; no entanto, o desenvolvedor do algoritmo trabalha no VBA e ele muda com frequência, portanto, eu preciso me manter sincronizado com a versão do T-SQL e validar novamente a cada mudança.
O T-SQL é a única maneira de implementar funções baseadas em conjunto?
fonte
N
lotes e executarN
instâncias do seu algoritmo emN
processadores / computadores separados. Por outro lado, qual é o seu principal gargalo - transferir os dados do SQL Server para o Excel ou cálculos reais? Se você alterar a função VBA para retornar imediatamente algum resultado fictício, quanto tempo levaria todo o processo? Se ainda demorar horas, o gargalo está na transferência de dados. Se demorar alguns segundos, você precisará otimizar o código VBA que faz os 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
No Management Studio esta função que é chamado para cada uma das linhas leva 50mS(FileID, RowID)
.Respostas:
No que diz respeito à metodologia, acredito que você está latindo para a b-tree errada ;-).
O que nós sabemos:
Primeiro, vamos consolidar e revisar o que sabemos sobre a situação:
Existe um procedimento armazenado chamado para cada linha:
A definição (pelo menos em parte) é:
O que podemos supor:
Em seguida, podemos analisar todos esses pontos de dados juntos para ver se podemos sintetizar detalhes adicionais que nos ajudarão a encontrar um ou mais gargalos e apontar para uma solução ou, pelo menos, descartar algumas soluções possíveis.
A direção atual dos comentários nos comentários é que o principal problema é a transferência de dados entre o SQL Server e o Excel. Esse é realmente o caso? Se o procedimento armazenado for chamado para cada uma das 800.000 linhas e demorar 50 ms por cada chamada (ou seja, por cada linha), isso adicionará até 40.000 segundos (não ms). E isso é equivalente a 666 minutos (hhmm ;-), ou pouco mais de 11 horas. No entanto, o processo todo levou apenas 7 horas para ser executado. Já temos 4 horas sobre o tempo total e ainda adicionamos tempo para fazer os cálculos ou salvar os resultados novamente no SQL Server. Então, algo não está bem aqui.
Observando a definição do Stored Procedure, existe apenas um parâmetro de entrada para
@FileID
; não há nenhum filtro ativado@RowID
. Portanto, suspeito que um dos dois cenários a seguir esteja acontecendo:@FileID
, que parece abranger aproximadamente 4000 linhas. Se as 4000 linhas retornadas declaradas forem uma quantidade bastante consistente, haverá apenas 200 delas agrupadas nas 800.000 linhas. E 200 execuções com 50 ms cada equivale a apenas 10 segundos nas 7 horas.@FileID
for passada levará um pouco mais para puxar novas linhas para o Buffer Pool, mas as próximas execuções 3999 normalmente retornarão mais rapidamente porque já estão sendo em cache, certo?Eu acho que incidindo sobre este "filtro" procedimento armazenado ou qualquer transferência de dados do SQL Server para o Excel, é um arenque vermelho .
No momento, acho que os indicadores mais relevantes de desempenho sem brilho são:
Eu suspeito que:
UPDATE
declarações separadas , que são 800.000 transações separadas.Minha recomendação (com base nas informações atualmente disponíveis):
Sua maior área de melhoria seria atualizar várias linhas ao mesmo tempo (ou seja, em uma transação). Você deve atualizar seu processo para trabalhar em termos de cada
FileID
um delesRowID
. Então:FileID
em uma matrizFileID
) tenham sido calculadas:RowID
Se o seu índice de cluster ainda não estiver definido como
(FileID, RowID)
você deve considerar isso (como @MikaelEriksson sugeriu em um comentário sobre a Pergunta). Isso não ajudará essas UPDATEs singleton, mas pelo menos melhoraria um pouco as operações agregadas, como o que você está fazendo nesse procedimento armazenado "filtro", pois todas elas são baseadasFileID
.Você deve considerar mover a lógica para uma linguagem compilada. Eu sugeriria a criação de um aplicativo .NET WinForms ou mesmo do console. Prefiro o Console App, pois é fácil agendar via SQL Agent ou Windows Scheduled Tasks. Não importa se é feito em VB.NET ou C #. O VB.NET pode ser um ajuste mais natural para o seu desenvolvedor, mas ainda haverá alguma curva de aprendizado.
Não vejo nenhuma razão neste momento para mudar para SQLCLR. Se o algoritmo for alterado com frequência, isso seria irritante e teria que reimplantar o Assembly o tempo todo. A reconstrução de um aplicativo de console e a colocação do .exe na pasta compartilhada adequada na rede, de modo que você execute o mesmo programa e sempre esteja atualizado, deve ser bastante fácil de fazer.
Eu não acho que mover o processamento totalmente para o T-SQL ajudaria se o problema é o que suspeito e você está apenas fazendo uma atualização de cada vez.
Se o processamento for movido para o .NET, você poderá usar TVPs (Parâmetros com Valor de Tabela) para passar a matriz para um Stored Procedure que chamaria um
UPDATE
que JOINs para a variável de tabela TVP e, portanto, é uma transação única. . O TVP deve ser mais rápido do que fazer 4000INSERT
s agrupados em uma única transação. Mas o ganho resultante do uso de TVPs acima de 4000INSERT
s em uma transação provavelmente não será tão significativo quanto a melhoria observada ao passar de 800.000 transações separadas para apenas 200 transações de 4000 linhas cada.A opção TVP não está disponível nativamente para o lado do VBA, mas alguém apresentou uma solução alternativa que pode valer a pena testar:
Como melhoro o desempenho do banco de dados ao passar do VBA para o SQL Server 2008 R2?
SE o processo de filtro estiver sendo usado apenas
FileID
naWHERE
cláusula, e se esse processo estiver realmente sendo chamado por cada linha, você poderá economizar algum tempo de processamento armazenando em cache os resultados da primeira execução e usando-os pelo restante das linhasFileID
, certo?Depois de conseguir o processamento feito por FileID , então podemos começar a falar de processamento paralelo. Mas isso pode não ser necessário nesse momento :). Dado que você está lidando com três partes não ideais bastante importantes: transações Excel, VBA e 800k, qualquer conversa sobre SSIS, paralelogramos ou quem sabe o que é otimização prematura / coisas do tipo carroça antes do cavalo . Se conseguirmos reduzir esse processo de 7 horas para 10 minutos ou menos, você ainda estaria pensando em outras maneiras de torná-lo mais rápido? Existe um prazo de conclusão que você tem em mente? Lembre-se de que, uma vez concluído o processamento em um FileID Por isso, se você tivesse um aplicativo de console do VB.NET (ou seja, linha de comando .EXE), não haveria nada impedindo a execução de alguns desses FileIDs por vez :), seja pela etapa CmdExec do SQL Agent ou pelas Tarefas agendadas do Windows, etc.
E, você sempre pode adotar uma abordagem em fases e fazer algumas melhorias de cada vez. Por exemplo, começando com as atualizações
FileID
e, portanto, usando uma transação para esse grupo. Então, veja se você consegue fazer o TVP funcionar. Em seguida, veja como pegar esse código e movê-lo para o VB.NET (e os TVPs funcionam no .NET para que sejam portados corretamente).O que não sabemos ainda pode ajudar:
ATUALIZAÇÃO 1:
** Parece haver alguma confusão sobre o que VBA (Visual Basic for Applications) e o que pode ser feito com ele, portanto, isso é apenas para garantir que estamos todos na mesma página da web:
ATUALIZAÇÃO 2:
Mais um ponto a considerar: como as conexões estão sendo tratadas? O código VBA está abrindo e fechando a conexão a cada operação ou abre a conexão no início do processo e fecha no final do processo (ou seja, 7 horas depois)? Mesmo com o pool de conexões (que, por padrão, deve estar habilitado para o ADO), ainda deve haver um grande impacto entre abrir e fechar uma vez, em vez de abrir e fechar 800.200 ou 1.600.000 vezes. Esses valores são baseados em pelo menos 800.000 UPDATEs mais 200 ou 800k EXECs (dependendo da frequência com que o procedimento armazenado do filtro está realmente sendo executado).
Esse problema de muitas conexões é mitigado automaticamente pela recomendação que descrevi acima. Ao criar uma transação e realizar todas as atualizações dentro dessa transação, você manterá essa conexão aberta e a reutilizará para cada uma
UPDATE
. Se a conexão é mantida aberta ou não a partir da chamada inicial para obter as 4000 linhas de acordo com o especificadoFileID
ou fechada após a operação "get" e aberta novamente para as UPDATEs, é muito menos impactante, pois agora estamos falando de uma diferença de 200 ou 400 conexões totais em todo o processo.ATUALIZAÇÃO 3:
Eu fiz alguns testes rápidos. Lembre-se de que este é um teste de pequena escala e não exatamente a mesma operação (puro INSERT vs EXEC + UPDATE). No entanto, as diferenças de tempo relacionadas à maneira como as conexões e transações são tratadas ainda são relevantes, portanto, as informações podem ser extrapoladas para causar um impacto relativamente semelhante aqui.
Parâmetros de teste:
Mesa:
Operação:
TRUNCATE TABLE dbo.ManyInserts;
(dada a natureza desse teste, executar o FREEPROCCACHE, FREESYSTEMCACHE e DROPCLEANBUFFERS não parecia agregar muito valor).Resultados:
Como você pode ver, mesmo que a conexão do ADO ao banco de dados já esteja sendo compartilhada em todas as operações, é garantido que o agrupamento em lotes usando uma transação explícita (o objeto ADO deve ser capaz de lidar com isso) é significativamente garantido (ou seja, mais de 2x melhoria) reduza o tempo total do processo.
fonte
IMHO e trabalhando com a suposição de que não é possível codificar novamente o sub VBA no SQL, você já pensou em permitir que o script VBA conclua a avaliação no arquivo do Excel e depois grave os resultados no SQL Server via SSIS?
Você pode ter o sub-início e o fim do VBA invertendo um indicador em um objeto do sistema de arquivos ou no servidor (se você já configurou a conexão para gravar novamente no servidor) e, em seguida, use uma expressão SSIS para verificar esse indicador.
disable
propriedade de uma determinada tarefa em sua solução SSIS (para que o processo de importação aguarde até que o sub VBA seja concluído, se você estiver preocupado com a possibilidade de ultrapassar sua agenda).Além disso, você pode ter o script VBA iniciado programaticamente (um pouco instável, mas eu usei a
workbook_open()
propriedade para disparar tarefas "disparar e esquecer" dessa natureza no passado).Se o tempo de avaliação do script VB começar a se tornar um problema, você poderá ver se o desenvolvedor do VB está disposto e é capaz de portar seu código em uma tarefa de script VB na solução SSIS - na minha experiência, o aplicativo Excel gera muita sobrecarga quando trabalhando com dados neste volume.
fonte