2009-11-10 41 views
3

我已经创建了一个Excel电子表格,它可以帮助从Oracle数据库进行数据分析。在Excel中取消外部查询VBA

用户输入,然后单击“刷新查询”按钮,该按钮为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 

有没有办法让用户手动取消查询(调用.CancelRefresh),而Excel的用户界面被冻结?

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

+0

好吧,我想我花了太多时间试图找到可能有效的解决方案。看到我下面的附加答案(这是一个奇怪的长答案)。 – 2009-11-12 17:38:42

回答

4

这里有一个我知道会工作的方法。但是,有一些并发症。

这里是它是如何做:

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

这里有一些并发症:

  • 我包括一个方法来提醒数据已udpated用户。在工作簿不可访问时,有时可能会尝试检查数据是否已更新,这迫使用户尝试更新值。我加入了一个叫做我的时间的方法,它暂停了代码的执行,所以它只检查每隔那么多秒。
  • 更新后的工作表将在新窗口中弹出,因此用户需要点击其原始工作表并继续工作。如果你熟悉Windows脚本,你可以学会隐藏这个(我还没有学过)。

这里有一些文件和代码。一定要阅读代码中的评论,了解为什么有些东西在那里。

文件: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实时加载安装。如果您安装了Office Live加载项,它将在退出时引发异常。

文件:C:\ RunExcel.bat

接下来,我们要创建一个批处理文件,将打开我们刚才提出的Excel文件。从批处理文件中调用Excel文件而不是直接从使用Shell的其他Excel文件中调用Excel文件的原因是,因为在其他应用程序关闭之前(至少在使用Excel.exe "c:\File.xls"时)Shell才会继续。但是,批处理文件运行其代码,然后立即关闭,从而允许调用它的原始代码继续。这是什么让你的使用继续在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调用到与原始文件不同的线程中。这使您可以在原始文件中工作,并在其他文件已更新时仍能收到警报。

祝你好运!

1

编辑:不是包括在这个相同的答案一个更完整的答案,我创建了完全致力于该解决方案的独立的答案。请在下面查看(或更高版本)

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

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

最后,你可能考证的DoEvents功能,产生执行回操作系统,以便它可以处理其他事件。我不确定它是否适用于您的情况,但您可以查看它。这样,当流程完成时,您可以做其他事情(这是很危险的,因为用户可以在流程运行时更改应用程序的状态)。


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

+0

Esc取消查询,并应该释放工作簿以及我的知识。我建议连接到我们的多维数据集的用户使用Esc,如果他们搞砸了并锁定。 – ajdams 2009-11-10 22:36:31

+0

不幸的是,这似乎并不奏效。 (请参阅主文章编辑) – Steven 2009-11-11 00:22:54

+0

我会在我的文章中提供更详细(但很复杂)的方法。 – 2009-11-11 03:09:32

0

那么,你可以考虑到以前时尚的方式 - 将查询为较小的批次与批次之间使用Do Events

0

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

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

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

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