我希望能够从MS Access VBA执行SQL Server存储过程,这样我可以读取(1)所有结果集,而不仅仅是第一个结果集; (2)PRINT语句或类似内容产生的任何消息。
我有一个带有一个输入参数的测试存储过程,它产生3个不同的结果集和大约90条消息。它调用了几个子存储过程,我可以从SSMS中很好地执行它,但是(对我来说)尚不清楚如何从Access VBA中做到最好。到目前为止,我已经尝试了以下方法:
DAO。使用SQL直通查询,虽然有些笨拙,但我可以在DAO中得到很多想要的东西。它返回3个结果集中的第一个作为记录集,并且通过使用LogMessages属性,我可以获得一个包含发出的消息的表(“ Admin – NN”)。
ADO。使用Connection和Command对象,我可以从存储过程中获得代表第一个结果集的单个记录集。但是,我似乎无法说服它生产仅用于转发的记录集。关于消息,在某一点上,所有消息(至少,我预期的大约150条消息中的前127条)都进入了连接的Errors集合(!),但是当我将消息数量减少到大约90条时,它们都没有出现在我所能找到的任何地方。
正如我最初说的,我真正想要的是所有结果集,加上消息的输出。这可能吗?
以下是我当前用于执行存储过程的例程的列表:
Function ExecuteStoredProcedureADO(SPName As String, Connect As String, ReturnsRecords As Boolean, _
ParamArray Params() As Variant) As ADODB.Recordset
' v1.0 2018/06/26
' execute stored procedure SPName on a SQL Server database specified by the string in Connect
Dim strErr As String
Dim i As Integer
Dim lngRecsAffected As Long
Dim cnn As ADODB.Connection
Dim cmd As ADODB.Command
Dim errCurr As ADODB.Error
Dim rst As ADODB.Recordset
On Error GoTo Catch
Set ExecuteStoredProcedureADO = Nothing
Set cnn = New ADODB.Connection
cnn.Errors.Clear
cnn.mode = adModeRead
cnn.CommandTimeout = 300
cnn.Open Connect
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = cnn
.CommandText = SPName
.CommandType = adCmdStoredProc
For i = 0 To UBound(Params) Step 4
.Parameters.Append .CreateParameter(Params(i), Params(i + 1), adParamInput, Params(i + 2), Params(i + 3))
Next i
Set rst = New ADODB.Recordset
rst.CursorType = adOpenStatic
If ReturnsRecords Then
'''Set rst = .Execute(lngRecsAffected)
rst.Open cmd, , adOpenStatic, adLockReadOnly
Else
Set rst = .Execute(, , adExecuteNoRecords)
End If
End With
If ReturnsRecords Then Set ExecuteStoredProcedureADO = rst
Final:
On Error Resume Next
If Len(strErr) > 0 Then Call AppendMsg(strErr)
Set rst = Nothing
Set cmd = Nothing
Exit Function
Catch:
If cnn.Errors.Count > 0 Then
With cnn
For Each errCurr In cnn.Errors
strErr = strErr & "Error " & errCurr.Number & ": " & errCurr.Description _
& " (" & errCurr.Source & ")" & vbCrLf
Next errCurr
strErr = Left(strErr, Len(strErr) - 2) ' truncate final CRLF
End With
Else
strErr = "Error " & Err.Number & ": " & Err.Description & " (" & Err.Source & ")"
End If
MsgBox strErr, vbOKOnly, gtitle
Resume Final
End Function
附录:关于多个结果集,我希望http://msdn.microsoft.com/en-us/library/ms677569%28VS.85%29.aspx 将有所帮助。
答案 0 :(得分:2)
要毫不客气地piggy带@Erik,您想创建一个新类来处理您的处理。类似于cProcedureHandler
。在此类中,您需要使用ADODB.Connection
关键字声明一个WithEvents
对象:
Dim WithEvents cn As ADODB.Connection
然后,您需要编写一个InfoMessage
事件处理程序,该事件处理程序将处理多个print语句。可以在here中找到有关InfoMessage
事件的信息,并且可以在here中找到使用连接的Errors
集合。因此,您最终将得到如下结果:
Private Sub cn_InfoMessage(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
Dim err As ADODB.Error
Debug.Print cn.Errors.Count & " errors"
For Each err In cn.Errors
' handle each error/message the way you need to.
Debug.Print err.Description
Next err
End Sub
由于您已经处理了多个消息,因此现在只需要处理多个记录集,这在您提供的链接中有很好的解释。我注意到的一件事是,示例链接使用rs is nothing
来检查是否没有更多的记录集,这对我不起作用。我必须使用rs State
属性。所以我最后得到了这个:
Public Sub testProcedure()
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim recordSetIndex As Integer
Set cn = modData.getConnection
Set cmd = New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "dbo.sp_foo"
Set rs = cmd.Execute
recordSetIndex = 1
Do Until rs.State = ObjectStateEnum.adStateClosed
Debug.Print "contents of rs #" & recordIndex
Do Until rs.EOF
Debug.Print rs.Fields(0) & rs.Fields(1)
rs.MoveNext
Loop
Set rs = rs.NextRecordset
recordSetIndex = recordIndex + 1
Loop
cn.Close
Set rs = Nothing
Set cn = Nothing
Set cmd = Nothing
End Sub
然后,当您准备从VBA运行SP时,只需执行以下操作:
set obj = new cProcedureHandler
obj.testFooProcedure
另一件事(您可能已经做过):确保SQL Server中的实际存储过程将nocount设置为打开。