2012-07-12 104 views
3

这似乎是一个相当普遍的问题,但是我发现的解决方案似乎都不起作用。强制vba在继续之前等待sql查询执行

我从SQL Server获取一些数据并将其复制到工作表中。然后我想从新数据中复制一个范围,并用它来做其他事情。所有这些都发生在一个单独的vba函数中。

我的问题是,当函数从Excel运行时,它将移动到函数的第二部分,而不用等待查询返回所需的数据。

当然,当我从vba IDE运行它时,该函数可以正常工作。

Dim a As New ADODB.Connection 
Dim r As New ADODB.Recordset 

a.Open (connStr) 
Set r = a.Execute(sqlstr) 

sht.Range("A2").CopyFromRecordset r 

'please wait here until the proc has executed? 

checkData = sht.Range("A2").Value 

当我运行从Excel checkData功能始终是空的,当我与F5它始终拥有所需的数据运行它。

+0

你试图做这个工作表中的功能?它不起作用,因为UDF只能返回一个值 - 它不能直接更新工作表。 – 2012-07-12 14:24:28

+0

@TimWilliams,是的我在vba函数中这样做,其中sqlStr是一个返回所需数据的sql存储过程。 – Skrealin 2012-07-12 14:43:31

+0

然后只要使用'checkData = r.Fields(0).Value',除非你需要的记录集中有更多的数据。如果你需要其他数据,那么你将不得不寻找某种方式来运行代码,而不是通过UDF。 – 2012-07-12 14:54:21

回答

2

这是行不通的?

Dim a As New ADODB.Connection 
Dim r As New ADODB.Recordset 

a.Open (connStr) 
Set r = a.Execute(sqlstr) 

Do 
    'Wait 
Loop Until Not r Is Nothing 

sht.Range("A2").CopyFromRecordset r 

checkData = sht.Range("A2").Value 

或者,如果失败,你可以尝试测试R的某些属性,如EOF或BOF,如果发生错误,或者你获得意想不到的价值,你知道数据还没有被载入。例如:

Dim a As New ADODB.Connection 
Dim r As New ADODB.Recordset 

a.Open (connStr) 
Set r = a.Execute(sqlstr) 

On Error Resume Next 
Do 
    Err.Clear 
    r.EOF 'Put your test here, you might test rowcount or similar. 
     'I've simply asked for the EOF property and ignored the result, I'm 
     'not sure if this will work in your case. Some testing may be required. 
While Err.Num <> 0 
On Error GoTo 0 'Or whatever you previously had this set to 

sht.Range("A2").CopyFromRecordset r 

checkData = sht.Range("A2").Value 
+0

感谢@mkingston的企图。不幸的是,循环似乎没有任何区别。对于第二个解决方案,我已经检查数据是否在作为标准ado片段的一部分执行CopyFromRecordSet之前返回,似乎函数正在后台运行它并继续到checkData。 – Skrealin 2012-07-12 14:41:43

+1

@Skrealin我认为这可能太明显了。我建议在你的copyfromrecordset函数中加入一些debug.print行来检查它实际上是否到达代码的每一部分。如果是,输出变量值来检查一切正确的读取/处理。 – mkingston 2012-07-12 14:44:56

+2

我认为代码的运行次数以某种方式运行,并且发现它不是。我认为在别人的环境中使用脚本语言时尤其如此;通常潜在的对象正在做你不指望的事情,如果你对它们没有很好的理解,很容易错过。 – mkingston 2012-07-12 14:47:18

2

这可能有所帮助。不要在代码中设置数据源,而要在目标工作表上将其设置为数据连接(Excel菜单数据|来自其他源|等)。一旦命名一个连接对象“(默认)”创建您点击它的代码沿着这些路线:

With ActiveWorkbook 
    .Connections("(Default)").OLEDBConnection.BackgroundQuery = False 
    .Connections("(Default)").OLEDBConnection.CommandText = sqlstr 
    .RefreshAll 
    ' do more stuff 
    ' will wait for .RefreshAll to complete because .BackgroundQuery = false 
    End With 
1

我想你需要经过r.movelast的执行,以确保返回所有行。

喜欢的东西

Set r = a.Execute(sqlstr) 

If Not r.EOF Then 
    r.MoveLast 
End If 

sht.Range("A2").CopyFromRecordset r 
1

随着安迪holaday的线,我得到这个通过取消选中“启用后台刷新”,在外部数据区域属性工作。禁用此功能会迫使excel在查询运行时等待。

7

尝试使用:

Application.CalculateUntilAsyncQueriesDone 

您执行SQL后,但你复制记录集之前

Set r = a.Execute(sqlstr) 
    Application.CalculateUntilAsyncQueriesDone 
sht.Range("A2").CopyFromRecordset r 
+0

其实这应该被标记为正确的答案。 我在网络查询刷新时遇到了同样的问题,即在从网站下载数据之前执行后续计算。在添加语句Applicaton.CalculateUntilAsnycQueriesDone之后,它正在等待,直到Web查询刷新完成。 – 2017-11-22 12:45:27

0

但是,如果没有返回,我发现这个代码会等到SQL记录代码在返回到下一个VBA语句之前返回。当一个命令依赖于另一个命令时,或者在继续之前需要创建整个数据集时,方便使用。

Dim Con As ADODB.Connection 
Dim CmdTxt As String 

Set Con = New Connection 
Con.ConnectionString = ThisWorkbook.GetYourConnectString() 
Con.Open 

CmdTxt = "EXEC db.schema.StoredProc1 @Param1 = 'Yes'" 
ExecuteSql Con, CmdTxt, True, True 

CmdTxt = "EXEC db.schema.StoredProc2 @Param1 = 'No'" 
ExecuteSql Con, CmdTxt, True, True 

MsgBox "Both commands completed sequentially" 

为的ExecuteSQL的代码是:

Public Function ExecuteSql(Con As ADODB.Connection, sql As String, _ 
    Optional StopOnError As Boolean = True, _ 
    Optional WaitUntilDone As Boolean = False) As String 

Dim cmd As ADODB.Command 
Set cmd = New ADODB.Command 

With cmd 

    .CommandType = 1 
    .CommandText = sql 
    .ActiveConnection = Con 

    If WaitUntilDone = True Then 
     .CommandTimeout = 0 'set timeout to unlimited 
     .Execute , , adExecuteNoRecords 'no records value speeds up internal code 
    Else 
     .Execute 
    End If 

End With 

ExecuteSql = "" 

Exit Function 
相关问题