我创建了一个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之前或之后)不会改变此行为。
答案 0 :(得分:4)
这是我知道可行的方法。但是,有一些并发症。
以下是它的完成方式:
以下是一些并发症:
以下是一些文件和代码。请务必阅读代码中的注释,了解为什么有些内容。
文件: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 Add-in,则在此文件关闭时发现了一个错误。如果安装了Office Live Add-in,则会在退出时抛出异常。
文件: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 Scripting非常方便,那么您可以使用隐藏模式打开窗口或传递文件名或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
正如您所看到的,我不断更新“Listening Cell”中的公式,以查看其他单元格何时更新。一旦更新了数据工作簿,我不确定如何在不重写所有单元格的情况下强制更新代码。关闭工作簿并重新打开它应该刷新值,但我不确定在代码中执行它的最佳方法。
整个过程有效,因为您使用批处理文件将Excel调用到与原始文件不同的线程中。这允许您在原始文件中工作,并在更新其他文件时仍然收到警报。
祝你好运!答案 1 :(得分:1)
编辑:我没有在同一个答案中包含更完整的答案,而是创建了一个完全专注于该解决方案的单独答案。请在下方查看(如果投票,则在上面查看)
您的用户可以通过按键盘上的Ctrl + Break来中断VBA功能。但是,我发现这可能会导致函数随机中断,直到每次运行任何函数。重新启动计算机时它会消失。
如果你在一个新的Excel实例中打开这个文件(意思是,转到开始&gt;程序并从那里打开Excel),我认为唯一将被冻结的工作簿将是执行代码的工作簿。 Excel的其他内容不应受到影响。
最后,您可能会研究DoEvents函数,这会将执行返回到操作系统,以便它可以处理其他事件。我不确定它是否适合你的情况,但你可以调查一下。这样你就可以在流程完成时做其他事情(这有点危险,因为用户可以在流程运行时更改应用程序的状态)。
我相信我知道一种实际可行的方法,但它很复杂,我没有在我面前的代码。它涉及在代码中创建Excel应用程序的单独实例,并将处理程序附加到该实例的执行。您将代码的DoEvents部分包含在应用程序关闭后释放的循环中。另一个实例化的Excel应用程序的唯一目的是打开文件以执行脚本然后关闭它自己。我以前做过这样的事情所以我知道它有效。我明天是否可以找到代码并添加它。
答案 2 :(得分:0)
好吧,您可以考虑采用旧式方式 - 将查询拆分为较小的批次,并在批次之间使用Do Events
。
答案 3 :(得分:0)
您可以尝试XLLoop。这使您可以在外部服务器上编写Excel函数(UDfs)。它包括许多语言的服务器实现(例如Java,Ruby,Python,PHP)。
然后,您可以连接到您的oracle数据库(并可能添加一个缓存层)并以这种方式将数据提供给您的电子表格。
XLL还具有弹出“忙”GUI的功能,该GUI允许用户取消功能调用(与服务器断开连接)。
顺便说一句,我参与了这个项目,如果您有任何问题,请告诉我。