excel - 使用 Raiserror : Is this possible? 将多步 ADO 查询的进度传递到 VBA

标签 excel tsql adodb vba

前言

我提出这个问题是为了符合 SO 询问指南,但如果您愿意,请随时建议批发重新设计。我可能正在使用一些不好的做法。

基本问题

我使用 ADO 执行需要几分钟才能执行的多步骤 SQL Server 查询。我用 Raiserror在我的 tsql 查询中让自己更详细地知道哪些步骤已经完成。是否可以在完整查询完成之前将这些消息传递给 VBA,同时仍继续查询?

详细信息和代码

我使用下面的 vba 来执行下面的 t-SQL 查询。如您所见,t-SQL 中出现了两个错误,分别显示“步骤 1 完成”和“步骤 2 完成”。我可以将这些消息(或交替使用错误号并将它们传递)以一种允许我检测它们并在继续执行查询的同时更新进度条的方式传递回 VBA 吗?

用于执行查询的 VBA:

    Set cmd = New ADODB.Command
    cmd.ActiveConnection = cnn
    cmd.CommandTimeout = 0
    cmd.CommandText = strQuery

    Set rst = New ADODB.Recordset
    rst.Open cmd
    'Go to the second to last recordset of the multi-step query
    String1 = Replace(strQuery, ";", "")
    For Loop2 = 1 To (Len(strQuery) - (Len(String1) + 1))
        Set rst = rst.NextRecordset
    Next Loop2

    'Copy results
    If Not rst.EOF Then
        (snip - actions)
    Else
        MsgBox "Error: No records returned."
    End If

精简的多步 tSQL 查询:
--#DRS1: The numbers being researched   
select distinct numbers 
into #DRS1
from Table1 (nolock)    
where numbers in ()


--#DRS1: Index
create nonclustered index Idx_DRS1
    on #DRS1(numbers);

Raiserror(“Step 1 complete”,1,1) with nowait;

--#DRS2: Table2 for numbers being researched
select distinct
 DRS1.numbers
,a.ID

into #DRS2

from #DRS1 DRS1

join Table2 (nolock) a  
    on DRS1.numbers = a.numbers

Raiserror(“Step 2 complete”,1,1) with nowait;


--MORE STEPS
(more steps)
(more raiserror statements)

澄清

我对以下内容不感兴趣:
  • 在查询完全完成之前不允许我更新进度条的方法。
  • 一种使用 Progress 的方法/MaxProgress ,因为据我了解,这将为查询中的每个步骤返回单独的数字,而不是为整个查询返回一个进度度量。

  • 我不太感兴趣:
  • 使用 # records affected消息来确定进度,因为某些步骤可能会返回与先前步骤相同数量的记录。

  • 研究

    我找到的最接近我正在寻找的东西是 here ,但作为该解决方案的讨论here说:

    This approach would only work for stored procedures that are not intended to return results, say procs that insert data into tables. Another approach would be needed if your stored proc returns a result set.



    由于我在查询的最后一步返回结果,以便在 Excel 中进行操作,我认为这对我不起作用。

    外部链接代码供引用

    SQL:
    CREATE PROCEDURE dbo.updTesting As
    
    Declare @RetVal integer
    
    Exec @RetVal = updTesting2
    Return @RetVal
    GO
    
    CREATE PROCEDURE dbo.updTesting2 As
    
    raiserror('Error From Testing 2 procedure',16,1)
    Return -2
    GO
    

    VBA:
    Private Sub Command1_Click()
        On Error GoTo ErrorHandler
    
        Dim db As ADODB.Connection
        Dim cmd As ADODB.Command
    
        Set db = New ADODB.Connection
        db.CursorLocation = adUseClient
        db.Open "provider=sqloledb;data source=handel;initial catalog=northwind;integrated security=sspi"
    
        Set cmd = New ADODB.Command
    
        With cmd
            Set .ActiveConnection = db
            .CommandText = "updTesting"
            .CommandType = adCmdStoredProc
            .Parameters.Append .CreateParameter("@RetVal", adInteger, adParamReturnValue)
            .Execute , , adExecuteNoRecords
        End With
    
    ExitPoint:
        On Error Resume Next
        Set cmd.ActiveConnection = Nothing
        Set cmd = Nothing
    
        db.Close
        Set db = Nothing
    
        Exit Sub
    
    ErrorHandler:
        MsgBox "Error # " & Err.Number & vbNewLine & vbNewLine & Err.Description
        Resume ExitPoint
    End Sub
    

    最佳答案

    有几种可能性可以为您的问题制定解决方案:

    (1) 在查询运行时捕获错误消息。这就是要求的方法。

    (2) 将大而长的查询分解成几个较小的 block 并一个接一个地运行它们。像这样,您知道哪个部分已完成,并且您可以在将下一个 block 发送到服务器之前根据该信息更新进度条。

    (3) 将大而长的查询更新为 log它在临时表中的服务器上的进度,然后在另一个查询仍在运行时读出此日志。

    虽然我建议仅在发生错误时才使用错误,而不是“滥用”它们进行日志记录、跟踪或反馈,但两个选项(1 和 2)对于 events 来说都是非常可行的。 :

    类似于 Worksheet事件 Worksheet_Change , Worksheet_Activate , 或 Worksheet_BeforeDoubleClick还有ADODB ADODB.Connection 的事件和 ADODB.Recordset .两者都有很好的文档,可以通过 (1) 添加对 Microsoft ActiveX Data Objects x.x Library 的引用在 VBE 中轻松查看。 (2) 按F2 (3)在顶部下拉菜单中选择ADODB库(4)最后查找RecordsetConnection类内。以下是 Connection 的可用事件:

    enter image description here

    如您所见,所有事件都标有闪电。要捕获/使用这些事件,您需要创建 Class Module在 VBE 中添加以下行:

    Dim WithEvents adoConnection As ADODB.Connection
    

    之后,您可以使用新创建的ADODB.Connection事件并从列表顶部选择所需的事件:

    enter image description here

    选项 (1) 的适用事件是 InfoMessage event每当在 ConnectionEvent 操作期间发生警告时,就会发生“[...]”。这里的导入部分是期间一个连接。因此,只要 ADODB 连接“遇到”错误,此事件就会自动触发。

    当然,这意味着必须向服务器发送原始查询 没有 等待答案。相反,您应该使用上述事件在查询执行时捕获任何错误,并创建另一个事件以在 entire query completed 时自动触发。 .

    有关异步 ADODB 连接的更多帮助以及它们可能存在的问题,您可能希望在此处查看以下两篇文章:

    ExecuteComplete ADODB Connection event not fired with adAsyncExecute parameter

    Running multiple async queries with ADODB - callbacks not always firing

    类似的方法可以与上述选项 (3) 和异步 ADODB 连接一起使用。

    让我知道这是否解决了您的问题或您有任何其他问题。

    全部可用ADODB事件可以在这里查看 https://msdn.microsoft.com/en-us/library/ms675083%28v=vs.85%29.aspx

    关于excel - 使用 Raiserror : Is this possible? 将多步 ADO 查询的进度传递到 VBA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37057191/

    相关文章:

    sql - 插入时出现 TSQL 错误 "String or binary data would be truncated"

    mysql - 如何通过ADODB读取2个表

    VBA - 在运行 SQL 查询时更新进度条

    excel - 如何使用excel公式得到这样的结果

    VBA 在图表(条形图)上写入值

    vba - Excel 64 位和 comdlg32.dll 自定义颜色

    oracle - 是否在Microsoft Access中拦截Oracleraise_application_error自定义消息?

    image - 将 Internet 中的图像添加到 VBA 图像控件中

    sql-server - 为什么 SQL 函数比 UDF 快

    sql - 如何一次性删除SQL Server数据库中的所有存储过程?