在Excel VBA中取消外部查询

我已经创build了一个Excel电子表格,帮助从Oracle数据库进行数据分析。

用户input然后单击“刷新查询”button,该button为Oracle生成一个查询来执行。 查询需要一分钟左右才能完成。 虽然VBA代码不挂在“。刷新”,所有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 

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

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

以下是我知道的方法。 但是,有一些并发症。

这是如何完成的:

  • 将电子表格与数据放在一个单独的工作簿中。 此工作表应该在打开时执行刷新查询,然后在数据更新后closures。
  • 创build一个batch file来调用“数据”Excel文件。
  • 在不同的工作簿中,创build一个供用户调用的过程(macros)。 此过程将调用batch file,随后调用Excel文件。 由于您直接调用batch file而不是Excel,所以Excel过程将继续执行,因为命令shell程序被如此快速地释放,并在另一个线程中打开另一个Excel文件。 这使您可以继续在主Excel文件中工作。

这里有一些复杂的:

  • 我提供了一种方法来提醒用户数据已被清空。 有时间问题可以尝试检查数据是否在工作簿不可访问时更新,这迫使用户尝试更新值。 我包括一个叫做我的时间的方法,它暂停了代码的执行,所以它只检查每隔那么多秒。
  • 更新的工作表将popup一个新的窗口,所以用户需要点击原来的工作表并继续工作。 如果你对Windows脚本很熟悉,你可以学习隐藏这个(我还没有学过)。

这里有一些文件和代码。 请务必阅读代码中的注释,了解为什么有些事情在那里。

FILE: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加载项,则在closures此文件时发现了一个错误。 如果您安装了Office Live加载项,它将在退出时引发exception。

FILE:C:\ RunExcel.bat

接下来,我们将创build一个batch file,打开我们刚刚创build的Excel文件。 之所以从batch file中调用Excel文件,而不是从使用Shell的其他Excel文件中直接调用Excel文件,是因为除非另一个应用程序closures(至less在使用Excel.exe "c:\File.xls" ),Shell才会继续。 。 然而,batch file运行其代码,然后立即closures,从而允许调用它的原始代码继续。 这是什么让你的使用继续在Excel中工作。

所有这个文件需要的是:

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

如果您使用Windows脚本,您会喜欢以隐藏模式打开窗口或传递文件名称或Excel位置的参数。 我用batch file保持简单。

FILE:C:\ UserWorkbook.xls

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

您需要在此工作簿中的一个单元格,您将从DataUpdate工作簿中检查“更新表已完成”单元格。 我在Sheet1中select了单元格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 

正如你所看到的,我不断更新“Listening Cell”中的公式来查看其他单元格何时更新。 一旦数据工作簿已经更新,我不知道如何强制更新代码而不重写所有的单元格。 closures工作簿并重新打开它应该刷新值,但我不确定在代码中执行的最佳方式。

整个过程的工作原理是使用batch file将Excel调用到与原始文件不同的线程中。 这允许您在原始文件中工作,并在其他文件更新时仍然收到警报。

祝你好运!

编辑:而不是在这个相同的答案包括一个更完整的答案,我已经创build了一个单独的答案完全专注于该解决scheme。 看看下面(或以上,如果它被表决)

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

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

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


我相信我知道一个实际上能工作的方式,但这很复杂,我没有在我面前的代码。 它涉及在代码中创buildExcel应用程序的单独实例,并将处理程序附加到该实例的执行中。 您将代码的DoEvents部分包含在一个循环中,一旦应用程序closures就会释放。 另一个实例化的Excel应用程序的唯一目的是打开一个文件来执行一个脚本,然后closures它自己。 我之前做过这样的事情,所以我知道它的工作原理。 我会看看我明天是否可以find代码并添加它。

那么,你可以考虑一下老式的方式 – 将查询分成更小的批次,并在批次间使用Do Events

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

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

XLL还具有popup“忙碌”graphics用户界面的function,用户可以取消该function调用(与服务器断开连接)。

顺便说一句,我在这个项目上工作,所以让我知道你是否有任何问题。