【问题标题】:Stored Procedure w/ Multiple Queries in VBAVBA中带多个查询的存储过程
【发布时间】:2017-09-23 20:50:07
【问题描述】:

首先让我说我对存储过程的了解非常基础。我知道如何在 VBA 中编写基本查询和返回值。我也知道您基本上可以从 SQL 中编写程序,这是我的知识分解的地方。我只是不需要做的足够多。无论如何,在 VBA 中,我运行了一系列查询。正如我在这个项目的概念阶段所担心的那样,从单个例程运行太多查询会产生一些实际的性能问题。这是例程。

Public Sub linePoll()

    errorPosition = "aofResults.linePoll"
    On Error GoTo errorTrap
    Err.Clear

    Dim rst As ADODB.Recordset
    Dim rstA As ADODB.Recordset
    Dim rstB As ADODB.Recordset
    Dim rstC As ADODB.Recordset
    Dim rstD As ADODB.Recordset
    Dim rstE As ADODB.Recordset
    Dim packQty As Integer
    Dim m As Integer
    Dim formFactor As Integer

    m = 0
    constr = "Provider=sqloledb;data source=i.p.add.ress;initial catalog=catalog;user id=user;password=password"
    Set conn = New ADODB.connection
    Set cmd = New ADODB.Command
    conn.Open constr
    cmd.ActiveConnection = conn
    LogDiagnosticsMessage "Getting order quantity"
    cmd.CommandText = "SELECT ISNULL(SUM(lQ.[QUANTITY]), 0) FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ LEFT JOIN [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_QUEUE] AS oQ ON lQ.[SALES_ORDER_NUMBER] = oQ.[SALES_ORDER_NUMBER]"
    Set rst = cmd.execute                        'Get total order quantity, may change if inventory depletes.
    If Not rst.EOF = True Then
        LogDiagnosticsMessage "Found order quantity as " & rst(0)
        Set eTag = ThisDisplay.eGroup.item("AOF\soQuantity")
        eTag.value = rst(0)
    Else
                LogDiagnosticsMessage "No order found in line queue DB"
    End If
    If Not rst(0) = 0 Then
        LogDiagnosticsMessage "Getting optic order quantity packed"
        cmd.CommandText = "SELECT Count(rL.SERIAL_NUMBER) FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_OPTIC_RESULTS] AS rL WHERE EXISTS (SELECT [SO_LINE_NUMBER] FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ LEFT JOIN [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_QUEUE] AS oQ ON lQ.[SALES_ORDER_NUMBER] = oQ.SALES_ORDER_NUMBER) AND [REJECT] = 'False'"
        Set rst = cmd.execute
        If Not rst.EOF = True Then
            LogDiagnosticsMessage "Found optics order quantity packed as " & rst(0)
            Set eTag = ThisDisplay.eGroup.item("AOF\soQuantityPacked")
            eTag.value = rst(0)
        Else
                    LogDiagnosticsMessage "No packed optics found in results DB"
        End If
        Set eTag = ThisDisplay.eGroup.item("Machine\itoSettings0") 'Evaluate packing quantity against machine settings (stored in DB, written to PLC at first startup)
        LogDiagnosticsMessage "Evaluating manual pack need"
        If rst(0) < eTag.value Then
            Set eTag = ThisDisplay.eGroup.item("AOF\manualPack") 'Evaluate packing quantity
            eTag.value = True
        Else
            eTag.value = False
        End If
        LogDiagnosticsMessage "Getting the top line order information"
        cmd.CommandText = "SELECT lQ.[SO_LINE_NUMBER],lQ.[QUANTITY],lQ.[SELECTED],lQ.[FORM_FACTOR_ID], lQ.[FINISHED_PART_NUMBER], ISNULL(lQ.[OEM_PART_NUMBER], ''),ISNULL(lQ.[COMPATIBILITY], ''), oQ.[INDIVIDUAL_PACKAGING], oQ.[SALES_ORDER_NUMBER] FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_QUEUE] AS oQ LEFT JOIN [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ ON oQ.[SALES_ORDER_NUMBER] = lQ.[SALES_ORDER_NUMBER] WHERE lQ.[SO_LINE_NUMBER] IS NOT NULL ORDER BY lQ.[SELECTED] DESC,lQ.[COMPLETED] ASC"
        Set rstA = cmd.execute()                     'Returns the line orders associated to the sales order
        If Not rstA.EOF = True Then
            Set eTag = ThisDisplay.eGroup.item("AOF\SOLineNumber")
            If Not eTag.value = rstA(0) Then        '************************************Updates the line order if the line order is different than what's already selected
                Set eTag = ThisDisplay.eGroup.item("AOF\SOLineNumber")
                eTag.value = rstA(0)
                Set eTag = ThisDisplay.eGroup.item("AOF\QuantityOrdered")
                eTag.value = rstA(1)
                Set eTag = ThisDisplay.eGroup.item("AOF\FinishedPartNumber")
                eTag.value = rstA(4)
                Set eTag = ThisDisplay.eGroup.item("AOF\OEMPartNumber")
                eTag.value = rstA(5)
                Set eTag = ThisDisplay.eGroup.item("AOF\Compatibility")
                eTag.value = rstA(6)
                Set eTag = ThisDisplay.eGroup.item("AOF\IndividualPack") 'PLC expects "Double Pack" true\false, ITO sends "Single Pack" true\false
                eTag.value = Not rstA(7)
            End If                                  '************************************
            LogDiagnosticsMessage "Getting the quantity of parts passed for this line order"
            cmd.CommandText = "SELECT COUNT(oL.[SERIAL_NUMBER]) FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_OPTICS] AS oL WHERE NOT EXISTS ( SELECT [SERIAL_NUMBER] FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_OPTIC_RESULTS] rL WHERE oL.[SERIAL_NUMBER] = rL.[SERIAL_NUMBER]) AND oL.[SO_LINE_NUMBER] = " & rstA(0)
            Set rstB = cmd.execute()                 'Returns the count of the parts associated to the above line order that passed
            If Not rstB.EOF = True Then
                Set eTag = ThisDisplay.eGroup.item("AOF\QuantityPassed")
                eTag.value = rstB(0)
                LogDiagnosticsMessage "Evaluating parts remaining for this line order as " & rstB(0)
                Select Case rstB(0)        'Evaluate Qty left to process in active line order
                    Case Is = 0                          'Qty Zero (Line order complete)
                            LogDiagnosticsMessage "Setting line order as completed"
                            cmd.CommandText = "UPDATE [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] SET [SELECTED] = 'False', [COMPLETED] = 'True' WHERE [SO_LINE_NUMBER] = " & rstA(0)
                            cmd.execute                      'Unselect the currently index line order and Set order as completed
                            LogDiagnosticsMessage "Checking line order quantity against sales order quantity"
                            cmd.CommandText = "SELECT ((SELECT COUNT(lQ.[SO_LINE_NUMBER]) FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ LEFT JOIN [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_QUEUE] AS oQ ON oQ.[SALES_ORDER_NUMBER] = lQ.[SALES_ORDER_NUMBER]) - (SELECT COUNT(lQ.[SO_LINE_NUMBER]) FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ LEFT JOIN [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_QUEUE] AS oQ ON oQ.[SALES_ORDER_NUMBER] = lQ.[SALES_ORDER_NUMBER] WHERE lQ.[COMPLETED] = 'True'))"
                            Set rstD = cmd.execute()         'Check line queue quantity associated to the sales order, count the line orders associated to the current sales order in the queue that are incomplete
                            'Set the currently indexed line order as selected
                            If rstD(0) = 0 Then
                            cmd.CommandText = "SELECT [ID] FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_BOXES] WHERE [SELECTED] = 'True'"
                            Set rstE = cmd.execute()
                                If Not rstE.EOF = False Then
                                    Set eTag = ThisDisplay.eGroup.item("AOF\soFinished")
                                    eTag.value = True
                                    Set eTag = ThisDisplay.eGroup.item("AOF\PLC_FinishBox")
                                    If eTag.value = 1 Then
                                        LogDiagnosticsMessage "Checking whether or not the PLC ready to complete the order"
                                        Set eTag = ThisDisplay.eGroup.item("AOF\PLC_CompleteOrder") 'Checks that order fulfillment mode is turned off
                                        If eTag.value = 1 Then
                                            boxNum = 0
                                            LogDiagnosticsMessage "Unselecting sales order " & rstA(8) & ", deleting results for this sales order, and setting status to ERP"
                                            cmd.CommandText = "UPDATE [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_QUEUE] SET [SELECTED] = 'False' WHERE [SALES_ORDER_NUMBER] = '" & rstA(8) & "'"
                                            cmd.execute          'Set's the current sales order selected bit to off
                                            cmd.CommandText = "UPDATE [ROBOTICS_OPTICS_MECHUAT].[dbo].[MACHINE_STATE] SET [STATUS] = 'USER' where [OPERATING_STATE] = 2"
                                            cmd.execute          'sets the status back to USER
                                            cmd.CommandText = "DELETE FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_OPTIC_RESULTS]"
                                            cmd.execute
                                            Set eTag = ThisDisplay.eGroup.item("AOF\orderFulfillmentMode")
                                            eTag.value = 0
                                            Set eTag = ThisDisplay.eGroup.item("AOF\PLC_CompleteOrder")
                                            eTag.value = 0
                                        End If
                                    End If
                                End If
                                rstE.Close
                            Else
                                rstA.MoveNext                'Index to the next line order in the record set
                                LogDiagnosticsMessage "Indexing to the next order in the list, setting" & rstA(0) & " selected."
                                cmd.CommandText = "UPDATE [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] SET [SELECTED] = 'True' WHERE [SO_LINE_NUMBER] = " & rstA(0)
                                cmd.execute
                                rstD.Close
                            End If
                    Case Is > 0                          'Qty Remaining > Line Order Qty (Line Order Select)
                        LogDiagnosticsMessage "Getting the form factor for this line order"
                        cmd.CommandText = "SELECT fF.[FORM_FACTOR_DESCRIPTION] FROM [ROBOTICS_OPTICS_MECHUAT].[dbo].[FORM_FACTOR] AS fF LEFT JOIN [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ ON lQ.[FORM_FACTOR_ID] = fF.[FORM_FACTOR_ID] WHERE lQ.[SELECTED] = 'True'"
                        Set rstC = cmd.execute()         'Returns the form factor description that is currently selected in the order line queue
                        If Not rstC.EOF = True Then
                            Set eTag = ThisDisplay.eGroup.item("AOF\FormFactor")
                            eTag.value = rstC(0)
                            Set eTag = ThisDisplay.eGroup.item("AOF\NextOpticXFP")
                            Select Case rstC(0)
                            Case Is = "XFP"
                                eTag.value = True
                            Case Is <> "XFP"
                                eTag.value = Not True
                            End Select
                        End If
                        rstC.Close
                        LogDiagnosticsMessage "Setting " & rstA(0) & " as selected"
                        cmd.CommandText = "UPDATE [ROBOTICS_OPTICS_MECHUAT].[dbo].[AOF_ORDER_LINE_QUEUE] SET [SELECTED] = 'True' WHERE [SO_LINE_NUMBER] = " & rstA(0)
                        cmd.execute                      'Set line as selected
                    Case Else
                    End Select
                Else
                    MsgBox ("Error: No line orders exist for sales order " & rstA(4) & ".")
                End If
        ElseIf rstA.EOF = True Then
            MsgBox ("Error: No sales order exists or no line orders associated to sales order: " & rstA(4) & " exists.")
        End If
    End If
    Set eTag = ThisDisplay.eGroup.item("AOF\ITO_OpticsReady")
    eTag.value = True

    GoTo cleanExit

cleanExit:
    On Error Resume Next
    If Not rst Is Nothing Then
        rst.Close
    End If
    If Not rstA Is Nothing Then
        rstA.Close
    End If
    If Not rstB Is Nothing Then
        rstB.Close
    End If
    If Not rstC Is Nothing Then
        rstC.Close
    End If
    If Not rstD Is Nothing Then
        rstD.Close
    End If
    If Not rstE Is Nothing Then
        rstE.Close
    End If
    conn.Close
    Exit Sub

errorTrap:
    LogDiagnosticsMessage "_Eventwatcher2.gfx, Position: " & errorPosition & " , Error Code: [ " & Hex(Err.number) & "], Description: " & Err.Description & ""
    Resume cleanExit

End Sub

所以,我的想法是在服务器端进行更多处理。我创建了单独的存储过程来封装这些查询中的大部分,但它对性能的影响不够强。我现在将我带到这里的主要想法是,我可以将所有这些放入一个存储过程中吗?如果我从一个存储过程执行七个查询,我如何在 VBA 代码中处理它?如何区分一个查询与下一个查询?如何将查询的输出用作存储过程中另一个查询的输入?

提前致谢!

编辑:所以我正在合并我使用 select 语句的查询。我最终得到了以下结果,这似乎有效。

Private Sub Button1_Released() 

    Dim rst(1 To 8) As ADODB.Recordset

    errorPosition = "ThisDisplay.Button1 Test"
    On Error GoTo errorTrap
    Err.Clear


    Set conn = New ADODB.Connection
    Set cmd = New ADODB.Command
    constr = "Provider=sqloledb;data source=i.p.add.ress;initial catalog=CATALOG;user id=user;password=pass"
    conn.Open constr

    With cmd
        .ActiveConnection = conn
        .CommandText = "rt_test"
        .CommandType = adCmdStoredProc
        .CommandTimeout = 2
    End With

    Set rst(1) = cmd.Execute()
    For i = 2 To 8
        Set rst(i) = rst(i - 1).NextRecordset
    Next
    i = 1
    GoTo cleanExit

cleanExit:
    On Error Resume Next
    For i = 1 To 8
        If Not rst(i) Is Nothing Then
            rst(i).Close
        End If
    Next
    conn.Close

    Exit Sub

errorTrap:
    LogDiagnosticsMessage "_Eventwatcher2.gfx, Position: " & errorPosition & " , Error Code: [ " & Hex(Err.Number) & "], Description: " & Err.Description & ""
    Resume cleanExit

End Sub

最终更新:

对上述内容稍作改动即可获得我需要的结果。事实证明,当调用 NextRecordSet 方法时,ADO 会自动关闭前一个记录集。因此,我需要打开记录集,然后将其克隆到数组中,然后移动到下一个记录集。 我添加了另一个记录集作为占位符。

Dim recordSet, rst(1 To 8) As ADODB.recordSet

然后修改我的代码如下:

conn.CursorLocation = adUseClient 'Needed to index through and clone recordsets
conn.Open constr

With cmd 'Run stored procedure
    .ActiveConnection = conn
    .CommandText = "rt_test"
    .CommandType = adCmdStoredProc
    .CommandTimeout = 2
End With

Set recordSet = cmd.execute()

For i = 1 To 8
    Set rst(i) = recordSet.Clone
    Set recordSet = recordSet.NextRecordset
Next
...
...
...
cleanExit:
    On Error Resume Next

    If Not recordSet Is Nothing Then
        recordSet.Close
    End If

    For i = 1 To 8
        If Not rst(i) Is Nothing Then
            rst(i).Close
        End If
    Next
    conn.Close

    Exit Sub

errorTrap:
    LogDiagnosticsMessage "_Eventwatcher2.gfx, Position: " & errorPosition & " , Error Code: [ " & Hex(Err.number) & "], Description: " & Err.Description & ""
    Resume cleanExit

End Sub

【问题讨论】:

  • 可能更好的方法是尝试获取所需的最终结果集,并且只有在失败时才尝试通过更多查询来检测原因。

标签: sql sql-server vba stored-procedures


【解决方案1】:

恐怕这比你想象的要复杂一些。

性能问题可能是由于查询本身,而不是它们的执行方式。如果从 SSMS 运行所有程序,性能如何?好的,类似于从 Excel 运行它们?也许你需要做一些调整——这不一定是容易的任务,但有一些工具可以自动完成。在显示单个查询的估计执行计划时,看看该工具是否会推荐一个可能有很大帮助的索引。

您可以运行返回多个记录集的 SP。通常你会得到一个对象——RecordSet——通常是第一个查询的结果。您可以正常使用它,一旦完成调用 NextRecordset 方法,您将可以访问第二个查询的 RecordSet,依此类推。

不幸的是,T-SQL 部分看起来比仅仅回答您的问题要多得多。

【讨论】:

  • 我正在考虑的是将所有查询合并到一个 SP 中,并执行一次,而不是创建多个查询。我觉得我的性能问题可能是网络速度。如果我只需要查询一次,问题可能会消失。在我写完这篇文章后我意识到这些问题可能是由于我正在从通过 VPN 连接到客户网络的虚拟机上进行测试。我愿意打赌,如果我在服务器本地,我会看到性能显着提高。你指的是什么工具?
  • 如果我编写一个包含多个查询的 SP,我如何在 SP 内的其他查询中使用一个查询的结果?
  • 几个选项。最容易执行的是将其生成临时表,因此,如果您有类似 SELECT ... FROM .... 的查询,请将其更改为 SELECT ... INTO #someName FROM ... .... 和然后在过程中需要的任何位置使用#someName,就像使用表格一样。 sp 完成后临时将被删除(实际上)。所以你可以将它与其他表等加入。
  • 谢谢。有没有一种简单的方法来移植我的代码,以便我可以将 rstA 设置为第二个记录集,将 rstB 设置为第三个记录集等?
  • 我可能会通过循环将字母更改为数字和索引。
猜你喜欢
  • 2014-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-05
  • 2013-07-23
  • 1970-01-01
  • 2014-09-16
  • 1970-01-01
相关资源
最近更新 更多