Kai*_*Kai 11 excel vba adodb excel-vba excel-2010
我有一个Excel工作簿,它向数据库发出三个查询以填充隐藏工作表上的三个表,然后运行三个"刷新"脚本将这些数据拉到三个可见的演示文稿表(每个查询一个).同步运行它非常慢:刷新的总时间是三个查询中每个查询的时间总和加上每个"刷新"脚本运行的时间总和.
我知道VBA不是多线程的,但我认为可以通过异步触发查询来加快速度(从而允许在执行时完成一些清理工作),并且然后在数据返回时为每个工作表执行填充/刷新工作.
我重写了我的脚本如下(请注意,我必须删除连接字符串,查询字符串等,并使变量通用):
Private WithEvents cnA As ADODB.Connection
Private WithEvents cnB As ADODB.Connection
Private WithEvents cnC As ADODB.Connection
Private Sub StartingPoint()
    'For brevity, only listing set-up of cnA here. You can assume identical
    'set-up for cnB and cnC
    Set cnA = New ADODB.Connection
    Dim connectionString As String: connectionString = "<my conn string>"
    cnA.connectionString = connectionString
    Debug.Print "Firing cnA query: " & Now
    cnA.Open
    cnA.Execute "<select query>", adAsyncExecute  'takes roughly 5 seconds to execute
    Debug.Print "Firing cnB query: " & Now
    cnB.Open
    cnB.Execute "<select query>", adAsyncExecute  'takes roughly 10 seconds to execute
    Debug.Print "Firing cnC query: " & Now
    cnC.Open
    cnC.Execute "<select query>", adAsyncExecute  'takes roughly 20 seconds to execute
    Debug.Print "Clearing workbook tables: " & Now
    ClearAllTables
    TablesCleared = True
    Debug.Print "Tables cleared: " & Now
End Sub
Private Sub cnA_ExecuteComplete(ByVal RecordsAffected As Long, ...)
    Debug.Print "cnA records received: " & Now
    'Code to handle the recordset, refresh the relevant presentation sheet here, 
    'takes roughly < 1 seconds to complete
    Debug.Print "Sheet1 tables received: " & Now
End Sub
Private Sub cnB_ExecuteComplete(ByVal RecordsAffected As Long, ...)
    Debug.Print "cnB records received: " & Now
    'Code to handle the recordset, refresh the relevant presentation sheet here, 
    'takes roughly 2-3 seconds to complete
    Debug.Print "Sheet2 tables received: " & Now
End Sub
Private Sub cnC_ExecuteComplete(ByVal RecordsAffected As Long, ...)
    Debug.Print "cnC records received: " & Now
    'Code to handle the recordset, refresh the relevant presentation sheet here, 
    'takes roughly 5-7 seconds to complete
    Debug.Print "Sheet3 tables received: " & Now
End Sub
典型的预期调试器输出:
Firing cnA query: 21/02/2014 10:34:22
Firing cnB query: 21/02/2014 10:34:22
Firing cnC query: 21/02/2014 10:34:22
Clearing tables: 21/02/2014 10:34:22
Tables cleared: 21/02/2014 10:34:22
cnB records received: 21/02/2014 10:34:26
Sheet2 tables refreshed: 21/02/2014 10:34:27
cnA records received: 21/02/2014 10:34:28
Sheet1 tables refreshed: 21/02/2014 10:34:28
cnC records received: 21/02/2014 10:34:34
Sheet3 tables refreshed: 21/02/2014 10:34:40
当然,这三个查询可以以不同的顺序返回,具体取决于哪个完成,因此有时典型的输出以不同的顺序排序 - 这是预期的.
然而,有时候,一个或两个cnX_ExecuteComplete回调根本不会发射.经过一段时间的调试,我很确定这样做的原因是,如果一个记录集在其中一个回调当前正在执行时返回,则不会发生调用.例如:
cnA_ExecuteComplete触发cnA_ExecuteComplete仍在运行,所以cnB_ExecuteComplete永远不要开火cnA_ExecuteComplete 在8时完成cnC_ExecuteComplete触发我的理论是否正确,这是问题所在?如果是这样,是否可以解决这个问题,或者调用"等待"直到当前代码执行而不是仅仅消失?
一种解决方案是在cnX_ExecuteComplete回调期间做一些非常快速的事情(例如,Set sheet1RS = pRecordset在运行刷新脚本同步之前检查它们是否全部完成),因此它们重叠的几率大约为零,但是想要先知道是否有更好的解决方案.
小智 9
我想我无法解释为什么你的'刷新脚本'并不总是激发.这是一种奇怪的行为,有时它们会运行,有时却不会运行.我无法真正看到您的整个脚本,但我可以向您展示我如何采用您的代码并使其每次都能正常工作.
注意:您的问题与使用adAsyncExecute参数未触发的ExecuteComplete ADODB Connection事件有某种关联
我在SQL服务器上添加了3个存储过程; sp_WaitFor5,sp_WaitFor10,sp_WaitFor20模拟的查询执行时间的延迟.
很简单
CREATE PROCEDURE sp_WaitFor5
AS
WAITFOR DELAY '00:00:05'
所有3个延迟.
然后在我的Module1我添加了一个非常简单的代码来调用自定义类
Option Explicit
Private clsTest As TestEvents
Sub Main()
    Cells.ClearContents
    Set clsTest = New TestEvents
    Call clsTest.StartingPoint
End Sub
然后我将类模块重命名为TestEvents并添加了稍微修改过的代码版本
Option Explicit
Private WithEvents cnA As ADODB.Connection
Private WithEvents cnB As ADODB.Connection
Private WithEvents cnC As ADODB.Connection
Private i as Long
Public Sub StartingPoint()
    Dim connectionString As String: connectionString = "Driver={SQL Server};Server=MYSERVER\INST; UID=username; PWD=password!"
    Debug.Print "Firing cnA query(10 sec): " & Now
    Set cnA = New ADODB.Connection
    cnA.connectionString = connectionString
    cnA.Open
    cnA.Execute "sp_WaitFor10", adExecuteNoRecords, adAsyncExecute
    Debug.Print "Firing cnB query(5 sec): " & Now
    Set cnB = New ADODB.Connection
    cnB.connectionString = connectionString
    cnB.Open
    cnB.Execute "sp_WaitFor5", adExecuteNoRecords, adAsyncExecute
    Debug.Print "Firing cnC query(20 sec): " & Now
    Set cnC = New ADODB.Connection
    cnC.connectionString = connectionString
    cnC.Open
    cnC.Execute "sp_WaitFor20", adExecuteNoRecords, adAsyncExecute
End Sub
Private Sub cnA_ExecuteComplete(ByVal RecordsAffected As Long, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, ByVal pConnection As ADODB.Connection)
    Debug.Print vbTab & "cnA_executeComplete START", Now
    For i = 1 To 55
        Range("A" & i) = Rnd(1)
    Next i
    Debug.Print vbTab & "cnA_executeComplete ENDED", Now
End Sub
Private Sub cnB_ExecuteComplete(ByVal RecordsAffected As Long, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, ByVal pConnection As ADODB.Connection)
    Debug.Print vbTab & "cnB_executeComplete START", Now
    For i = 1 To 1000000
        Range("B" & i) = Rnd(1)
    Next i
    Debug.Print vbTab & "cnB_executeComplete ENDED", Now
End Sub
Private Sub cnC_ExecuteComplete(ByVal RecordsAffected As Long, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, ByVal pConnection As ADODB.Connection)
    Debug.Print vbTab & "cnC_executeComplete START", Now
    For i = 1 To 55
        Range("C" & i) = Rnd(1)
    Next i
    Debug.Print vbTab & "cnC_executeComplete ENDED", Now
End Sub
我真的没有太大的改变,除了额外的参数进行Execute一些代码填充activesheet只是花时间.
现在,我可以运行不同的变体/配置.我可以旋转连接对象的执行时间.我可以有cnA5秒,cnB10秒,cnC20秒.我可以交换/调整每个_ExecuteComplete事件的执行时间.
从我自己的测试中我可以向你保证,所有3个都会被执行.
这里有一些基于类似于你的配置的日志
Firing cnA query(10 sec): 24/02/2014 12:59:46
Firing cnB query(5 sec): 24/02/2014 12:59:46
Firing cnC query(20 sec): 24/02/2014 12:59:46
    cnB_executeComplete START             24/02/2014 12:59:51 
    cnB_executeComplete ENDED             24/02/2014 13:00:21 
    cnA_executeComplete START             24/02/2014 13:00:21 
    cnA_executeComplete ENDED             24/02/2014 13:00:21 
    cnC_executeComplete START             24/02/2014 13:00:22 
    cnC_executeComplete ENDED             24/02/2014 13:00:22
在上面的示例中,您可以看到,所有3个查询都是异步触发的.
cnA5秒后返回句柄,这使得cnB第一个事件("刷新脚本")在层次结构中运行cnC时间最长.
由于首先cnB 回来,它会触发它的cnB_ExecuteComplete事件程序.它cnB_ExecuteComplete本身设置需要花费一些时间执行(迭代100万次并用随机数填充B列.注意:cnA填充A列,cnB col B,cnC col C).查看上面的日志需要30秒才能运行.
当cnB_ExecuteComplete正在执行其工作/占用资源(并且如您所知VBA是单线程的)时,cnA_ExecuteComplete事件将被添加到TODO进程的队列中.所以,你可以把它想象成一个队列.当事情正在被照顾下一件事情必须等到最后.
如果我改变配置; cnA5秒,cnB10秒,cnC20秒,然后让每个'刷新脚本'迭代100万次
Firing cnA query(5 sec): 24/02/2014 13:17:10
Firing cnB query(10 sec): 24/02/2014 13:17:10
Firing cnC query(20 sec): 24/02/2014 13:17:10
one million iterations each
    cnA_executeComplete START             24/02/2014 13:17:15 
    cnA_executeComplete ENDED             24/02/2014 13:17:45 
    cnB_executeComplete START             24/02/2014 13:17:45 
    cnB_executeComplete ENDED             24/02/2014 13:18:14 
    cnC_executeComplete START             24/02/2014 13:18:14 
    cnC_executeComplete ENDED             24/02/2014 13:18:44 
从第一个例子中清楚地证明了我的观点.
此外,尝试cnA5秒,cnB5秒,cnC5秒
Firing cnA query(5 sec): 24/02/2014 13:20:56
Firing cnB query(5 sec): 24/02/2014 13:20:56
Firing cnC query(5 sec): 24/02/2014 13:20:56
one million iterations each
    cnB_executeComplete START             24/02/2014 13:21:01 
    cnB_executeComplete ENDED             24/02/2014 13:21:31 
    cnA_executeComplete START             24/02/2014 13:21:31 
    cnA_executeComplete ENDED             24/02/2014 13:22:01 
    cnC_executeComplete START             24/02/2014 13:22:01 
    cnC_executeComplete ENDED             24/02/2014 13:22:31
这也完成/执行所有3.
就像我说的那样,我看不到你的整个代码,也许你的代码中某处有一个未处理的错误,也许有些东西误导你认为一个人_ExecuteComplete根本没有执行.尝试更改您的代码以反映我给您的代码并自行运行一些文本.我期待着您的反馈.
| 归档时间: | 
 | 
| 查看次数: | 8917 次 | 
| 最近记录: |