【问题标题】:Cancel External Query in Excel VBA在 Excel VBA 中取消外部查询
【发布时间】:2010-12-15 06:10:05
【问题描述】:

我创建了一个 Excel 电子表格,它有助于从 Oracle 数据库中进行数据分析。

用户输入然后单击“刷新查询”按钮,该按钮生成一个查询供 Oracle 执行。查询需要一分钟左右才能完成。尽管 VBA 代码不会挂在“.Refresh”上,但所有 Excel 窗口都将保持冻结状态,直到查询完成。

Sub refreshQuery_click()
    Dim queryStr as String

    ' Validate parameters and generate query   
    ' ** Code not included **
    '

    ' Refresh Query
    With ActiveWorkbook.Connections("Connection").OLEDBConnection
        .CommandText = queryStr
        .Refresh
    End With
End Sub

当 Excel 用户界面被冻结时,用户是否可以手动取消查询(调用 .CancelRefresh)?

编辑我不知道以下是否值得注意或常规行为。执行查询时,所有打开的 Excel 窗口(包括 VBA 编辑器)在任务管理器中变为“无响应”。按 Esc 和 Ctrl+Break 都不会取消脚本。此外,调用 DoEvents(在 .Refresh 之前或之后)不会改变这种行为。

【问题讨论】:

  • 好的,我想我花了太多时间试图找到一个可行的解决方案。请参阅下面的附加答案(该死的长答案)。

标签: vba excel


【解决方案1】:

编辑:我没有在同一个答案中包含更完整的答案,而是创建了一个完全专用于该解决方案的单独答案。请在下方查看(如果获得投票,请查看上方)

您的用户可以通过按键盘上的 Ctrl+Break 来中断 VBA 功能。但是,我发现这可能会导致您的函数随机中断,直到每次运行任何函数。重新启动计算机后它会消失。

如果您在新的 Excel 实例中打开此文件(即,转到开始 > 程序并从那里打开 Excel),我认为唯一将被冻结的工作簿将是执行代码的工作簿。 Excel 的其他实例不应受到影响。

最后,您可能会研究 DoEvents 函数,它会将执行返回给操作系统,以便它可以处理其他事件。我不确定它是否适用于您的情况,但您可以调查一下。这样,您可以在流程完成时做其他事情(这有点危险,因为用户可以在流程工作时更改应用程序的状态)。


我相信我知道一种实际可行的方法,但它很复杂,而且我面前没有代码。它涉及在代码中创建 Excel 应用程序的单独实例,并将处理程序附加到该实例的执行中。您将代码的 DoEvents 部分包含在一个循环中,该循环会在应用程序关闭后释放。另一个实例化的 Excel 应用程序的唯一目的是打开文件以执行脚本,然后自行关闭。我以前做过这样的事情,所以我知道它有效。我明天看看能不能找到代码并添加它。

【讨论】:

  • Esc 取消查询,据我所知也应该释放工作簿。我建议连接到我们多维数据集的用户在遇到问题并被锁定时使用 Esc。
  • 不幸的是,这似乎不起作用。 (见主帖编辑)
  • 我会在我的帖子中放一个更详细(但复杂)的方法。
【解决方案2】:

好吧,您可以考虑使用老式方法——将查询拆分成更小的批次,并在批次之间使用Do Events

【讨论】:

    【解决方案3】:

    我知道这是一种可行的方法。但是,也有一些并发症。

    这是如何完成的:

    • 将包含数据的电子表格放在单独的工作簿中。此工作表应在打开时执行刷新查询,然后在数据更新后关闭。
    • 创建一个批处理文件来调用“数据”Excel 文件。
    • 在不同的工作簿中,创建一个过程(宏)供用户调用。此过程将调用批处理文件,该文件随后调用 Excel 文件。由于您正在调用批处理文件而不是直接调用 Excel,因此 Excel 过程将继续进行,因为命令外壳被释放得如此之快,并在不同的线程中打开了另一个 Excel 文件。这使您可以继续在主 Excel 文件中工作。

    这里有一些并发症:

    • 我添加了一种方法来提醒用户数据已更新。存在时间问题,当工作簿不可访问时,可以尝试检查数据是否已更新,这会迫使用户尝试更新值。我包含了一个名为 my time 的方法,它会暂停代码的执行,因此它只会每隔这么多秒检查一次。
    • 更新的工作表将在新窗口中弹出,因此用户需要单击其原始工作表并继续工作。如果您对 Windows 脚本感到满意,您可以学会隐藏这一点(我还没有学过)。

    这里有一些文件和代码。请务必阅读代码中的 cmets,了解为什么会有一些东西。

    文件:C:\DataUpdate.xls

    我们将创建一个名为“DataUpdate.xls”的工作簿并将其放在我们的 C:\ 文件夹中。在 Sheet1 的单元格 A1 中,我们将添加抓取外部数据的 QueryTable。

    Option Explicit
    
    Sub UpdateTable()
    
    Dim ws As Worksheet
    Dim qt As QueryTable
    
    Set ws = Worksheets("Sheet1")
    Set qt = ws.Range("A1").QueryTable
    
    qt.Refresh BackgroundQuery:=False
    
    End Sub
    
    Sub OnWorkbookOpen()
    Dim wb As Workbook
    Set wb = ActiveWorkbook
    
    'I put this If statement in so I can change the file's
    'name and then edit the file without code
    'running.  You may find a better way to do this.
    If ActiveWorkbook.Name = "DataUpdate.xls" Then
    
    
        UpdateTable
        'I update a cell in a different sheet once the update is completed.
        'I'll check this cell from the "user workbook" to see when the data's been updated.
        Sheets("Sheet2").Range("A1").Value = "Update Table Completed    " & Now()
        wb.Save
    
        Application.Quit
    
    End If
    
    
    End Sub
    

    在 Excel 的 ThisWorkbook 对象中,有一个名为 Workbook_Open() 的过程。它应该如下所示,以便在打开时执行更新代码。

    Private Sub Workbook_Open()
    OnWorkbookOpen
    
    End Sub
    

    注意:如果 1) 您从命令行或 shell 访问该文件,并且 2) 您安装了 Office Live 插件,我在关闭此文件时发现了一个错误。如果您安装了 Office Live 加载项,它将在退出时引发异常。

    文件:C:\RunExcel.bat

    接下来,我们将创建一个批处理文件,用于打开我们刚刚制作的 Excel 文件。从批处理文件中调用 Excel 文件而不是使用 Shell 直接从其他 Excel 文件调用的原因是因为 Shell 在其他应用程序关闭之前不会继续(至少在使用 Excel.exe "c:\File.xls" 时)。但是,批处理文件运行其代码,然后立即关闭,从而允许调用它的原始代码继续执行。这将使您的用户继续在 Excel 中工作。

    这个文件只需要:

    cd "C:\Program Files\Microsoft Office\Office10\" 
    Excel.exe "C:\DataUpdate.xls"
    

    如果您熟悉 Windows 脚本,您可以做一些花哨的事情,例如以隐藏模式打开窗口或传递文件名或 Excel 位置的参数。我使用批处理文件保持简单。

    文件:C:\UserWorkbook.xls

    这是用户将打开以“进行工作”的文件。他们将调用代码来更新此工作簿中的另一个工作簿,并且在更新此工作簿时他们仍然可以在此工作簿中工作。

    您需要在此工作簿中的一个单元格中检查 DataUpdate 工作簿中的“更新表已完成”单元格。我在 Sheet1 中选择了单元格 G1 作为示例。

    将以下代码添加到此工作簿中的 VBA 模块:

    Option Explicit
    
    Sub UpdateOtherWorkbook()
    Dim strFilePath As String
    Dim intOpenMode As Integer
    Dim strCallPath As String
    Dim strCellValue As String
    Dim strCellFormula As String
    Dim ws As Worksheet
    Dim rng As Range
    
    Set ws = Worksheets("Sheet1")
    Set rng = ws.Range("G1")
    strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
    'This makes sure the formula has the most recent "Updated" value
    'from the data file.
    rng.Formula = strCellFormula
    
    strFilePath = "C:\RunExcel.bat"
    intOpenMode = vbHide
    
    'This will call the batch file that calls the Excel file.
    'Since the batch file executes it's code and then closes,
    'the Excel file will be able to keep running.
    Shell strFilePath, intOpenMode
    
    'This method, defined below, will alert the user with a
    'message box once the update is complete. We know that
    'the update is complete because the "Updated" value will
    'have changed in the Data workbook.
    AlertWhenChanged
    
    End Sub
    

    '

    Sub AlertWhenChanged()
    
    Dim strCellValue As String
    Dim strUpdatedCellValue As String
    Dim strCellFormula As String
    Dim ws As Worksheet
    Dim rng As Range
    
    Set ws = Worksheets("Sheet1")
    Set rng = ws.Range("G1")
    strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
    
    strCellValue = rng.Value
    strUpdatedCellValue = strCellValue
    
    'This will check every 4 seconds to see if the Update value of the
    'Data workbook has been changed.  MyWait is included to make sure
    'we don't try to access the Data file while it is inaccessible.
    'During this entire process, the user is still able to work.
    Do While strCellValue = strUpdatedCellValue
        MyWait 2
            rng.Formula = strCellFormula
        MyWait 2
            strUpdatedCellValue = rng.Value
        DoEvents
    Loop
    
    MsgBox "Data Has Been Updated!"
    
    End Sub
    

    '

    Sub MyWait(lngSeconds As Long)
    Dim dtmNewTime As Date
    
    dtmNewTime = DateAdd("s", lngSeconds, Now)
    
    Do While Now < dtmNewTime
        DoEvents
    Loop
    
    
    End Sub
    

    如您所见,我不断更新“侦听单元格”中的公式,以查看其他单元格何时更新。更新数据工作簿后,我不确定如何在不重写所有单元格的情况下强制更新代码。关闭工作簿并重新打开它应该会刷新值,但我不确定在代码中执行此操作的最佳方法。

    整个过程有效,因为您使用批处理文件将 Excel 调用到与原始文件不同的线程中。这使您可以在原始文件中工作,并且在其他文件更新时仍会收到警报。

    祝你好运!

    【讨论】:

      【解决方案4】:

      你可以试试XLLoop。这使您可以在外部服务器上编写 excel 函数 (UDfs)。它包括多种语言的服务器实现(例如 Java、Ruby、Python、PHP)。

      然后您可以连接到您的 oracle 数据库(并可能添加一个缓存层)并以这种方式将数据提供给您的电子表格。

      XLL 还具有弹出“忙碌”GUI 的功能,允许用户取消函数调用(与服务器断开连接)。

      顺便说一句,我在这个项目上工作,所以如果您有任何问题,请告诉我。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 2018-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多