2009-11-10 68 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還具有彈出「忙碌」圖形用戶界面的功能,該圖形用戶界面允許用戶取消函數調用(與服務器斷開連接)。

順便說一句,我在項目上工作,所以讓我知道你是否有任何問題。