在上一篇文章中,我介紹了如何將SQL Server中的數據導入Excel。其中,建立數據連接的過程是手工完成的,偶爾為之,可以接受,如果要反復操作,就不勝其煩。本文將介紹如何使用宏來自動化數據導入。
在本文中,我使用的是Excel 2007,所介紹的方法同樣適用於Excel 2010。為了使用宏,需要在Excel 2007中顯示Developer Tab。操作方法是在Excel選項對話框中,選中Show Developer tab in the Ribbon。
1. 用宏理解Excel
為了自動化數據導入過程,需要知曉Excel在該過程中執行了哪些操作。這可以通過錄制宏來捕獲。在Developer Tab下,點擊Record Macro,將宏命名為import_data_from_sqlserver。
按照上一篇文章中的方法建立數據庫鏈接。不同之處在於,在Import Data話框中,點擊Properties;在Connection Properties對話框中,將Command type修改為SQL,將Command text修改為select * from test.Stat。然后連續點擊OK,完成數據導入。
在Developer Tab下點擊Stop Recording,結束宏的錄制。
然后點擊Developer Tab的Visual Basic(或按下快捷鍵Alt+F11),打開的Microsoft Visual Basic視窗。在Project窗口中,雙擊Modules下的Module1,打開代碼編輯窗口。
在代碼編輯窗口中,可見宏import_data_from_sql_server的實現。
Sub import_data_from_sqlserver()
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array( _
"OLEDB;Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=localhostsqlexpress;Use Procedure for Prepa" _
, "re=1;Auto Translate=True;Packet Size=4096;Workstation ID=LIANGSHI;Use Encryption for Data=False;Tag with column collation when " _
, "possible=False;Initial Catalog=bug_db"), Destination:=Range("$A$1")). _
QueryTable
.CommandType = xlCmdSql
.CommandText = Array("select * from test.Stat")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.SourceConnectionFile = _
"C:Usersliangshi.000DocumentsMy Data Sourcesmytest.odc"
.ListObject.DisplayName = "Table_mytest"
.Refresh BackgroundQuery:=False
End With
End Sub
灰色代碼在當前數據薄的ListObjects容器中增加了一個對象,利用Source參數指定了OLEDB數據源,並提供了數據庫連接字符串,利用Destination參數指定了數據存放於何處。黃色代碼設置新增對象的QueryTable屬性。比較重要的設置是,設定QueryTable.CommandType為xlCmdSql(SQL查詢),設定QueryTable.CommandText為待運行的SQL查詢字符串,最后調用QueryTable.Refresh函數以執行查詢。
紅色代碼指定了數據庫服務器、數據庫和SQL查詢。只要能修改它們就可以在指定數據庫上運行指定查詢,以獲得我們需要的數據。這就是本文方法的技術基礎。只是宏import_data_from_sqlserver有一個缺點:它向固定區域($A$1)插入對象,因此不能反復運行。為了避免運行時錯誤,我的策略是:不重復建立數據庫連接,而是修改已有連接,從而獲得新的查詢結果。
2. Xlsm文件
從這里可以下載Book1.xlsm文件,它包含本文即將介紹的所有元素。該文件以xlsm后綴名,是因為這是一個包含宏的Excel文件。在打開它時,需要授權給宏(Macro)和數據連接(Data Connection)。
你如果厭倦了安全警告,可以將你的桌面加入“安全路徑”。那么從桌面打開的xlsm文件,將不顯示安全警告。
3. 界面:表格和按鈕
Book1.xlsm的布局如下圖所示。
- 左上角是一個表格,其中單元格B2對應數據庫服務器,單元格B3對應數據庫,單元格B4對應SQL查詢,它們的格式都是文本(Text)。這里使用表格是為了美觀,普通的單元格也不影響功能。
- 左下角是一個按鈕。它的插入方法是:Developer → Insert → Button。右擊該按鈕,可以進入編輯模式:調整大小、調整位置、修改名稱、設定宏等。
- 右下角是一個表格,它是導入的SQL Server的數據。選中該表格中的任意單元格(圖中是單元格C5),點擊Design Tab下的Properties,彈出External Data Properties對話框,點擊Connection Properties按鈕可以查看連接的詳細屬性。該連接是我手工建立的,其名稱是my_database_connection,通過SQL查詢從數據庫中獲得數據。
4. 實現邏輯:宏
按下快捷鍵Alt+F11,打開的Microsoft Visual Basic視窗。在Module1中可見宏RefreshDataConnection。它首先修改數據連接my_database_connection,主要修改內容是用單元格B2、B3和B4的值替換原有的數據庫服務器、數據庫和SQL查詢。然后,它調用數據連接my_database_connection的Refresh函數,以獲得新的數據。
Sub RefreshDataConnection()
With ActiveWorkbook.Connections("my_database_connection").OLEDBConnection
.BackgroundQuery = True
.CommandText = Array(" " & Range("B4").Value & " ")
.CommandType = xlCmdSql
.Connection = Array("OLEDB;Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;" _
, "Initial Catalog=" & Range("B3").Value & ";Data Source=" & Range("B2").Value & ";" _
, "Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=LIANGSHI;Use Encryption for Data=False;Tag with " _
, "column collation when possible=False")
.RefreshOnFileOpen = False
.SavePassword = False
.SourceConnectionFile = ""
.SourceDataFile = ""
.ServerCredentialsMethod = xlCredentialsMethodIntegrated
.AlwaysUseConnectionFile = False
End With
ActiveWorkbook.Connections("my_database_connection").Refresh
End Sub
按鈕Refresh對應的宏就是RefreshDataConnection,這是通過編輯按鈕的屬性來設置的。
這樣每次點擊按鈕Refresh就可以獲得最新的數據。例如,將查詢修改為SELECT * FROM test.Stat,並單擊Refresh,可得視圖Stat的查詢結果。
為了方便操作,我還在Sheet1中增加了宏Worksheet_Change,以響應Worksheet_Change事件。當Sheet1的內容發生變更時,Excel會調用宏Worksheet_Change。它會檢查被修改對象(Target)的單元格個數(Count)是否是1。如果是1,則進一步檢查被修改單元格是否是B4(即Row=4且Column=2),即SQL查詢是否被修改。如果SQL查詢被修改,則調用宏RefreshDataConnection以導入數據。有了這個宏,修改完SQL查詢,不用點擊Refresh,Excel就會自動加載新數據,非常方便。
值得一提的是,導入的數據表格是可以拷貝到其他數據薄的。被拷貝的內容包含已導入的數據和相關的數據庫連接。這意味着,在編寫完一個SQL查詢后,可以將被修改的數據連接“另存”到另一個數據薄中,以待未來使用,而這一切都可以用拷貝和粘貼來完成。
5. 小結
本文展示了一個宏的應用實例,並可以得出以下啟示。
- 利用宏可以了解Excel的實現機制,這有利於更好地(手工或自動地)操作Excel。
- 編寫宏的一般方法是將手工操作錄制為宏,然后對錄制的宏進行修改。
- Excel數據薄是一個可編程的GUI:每一個單元格可以看做一個輸入框,可以插入按鈕等控件以響應用戶輸入,其內建多種事件可以激發事件處理程序。
- 對於許多任務,你可以用Excel來完成,而不必編寫傳統意義上的程序。你可以編寫ASP.NET網站和Silverlight應用來提供用戶界面,那很了不起。但是,你的時間是有限的,而你的客戶也不喜歡網頁上緩慢的輸入。利用Excel的宏錄制,你可以在幾分鍾內完成一個GUI程序,並提供用戶最愛的Excel風格的輸入。
- 宏對於VSTO的一個優點是,它完全內建於Excel。你可以在秘書和前台的筆記本電腦上進行開發和調試,而那上面絕對不會有Visual Studio。此外,相比二進制的VSTO插件,可以輕松查看並修改的宏,可以滿足一些程序員個性化定制的需要。
編程並不局限於C#和Visual Studio。Excel也提供了便捷的編程環境,運用得當,會事半功倍。

![image_thumb[1] image_thumb[1]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vbGlhbmdzaGkvMjAxMjA0LzIwMTIwNDAzMDMxNDQ1MTY1OS5wbmc=.png)


![image_thumb[3] image_thumb[3]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vbGlhbmdzaGkvMjAxMjA0LzIwMTIwNDAzMDMxNDUxNjMzMS5wbmc=.png)


![image_thumb[9] image_thumb[9]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vbGlhbmdzaGkvMjAxMjA0LzIwMTIwNDAzMDMxNDU3Mjg0Ni5wbmc=.png)
![image_thumb[12] image_thumb[12]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vbGlhbmdzaGkvMjAxMjA0LzIwMTIwNDAzMDMxNTAxNDMyLnBuZw==.png)



![image_thumb[17]%204F326AB6 image_thumb[17]%204F326AB6](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vbGlhbmdzaGkvMjAxMjA0LzIwMTIwNDAzMDMxNTE0MTA5Ni5wbmc=.png)



![image_thumb[20] image_thumb[20]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vbGlhbmdzaGkvMjAxMjA0LzIwMTIwNDAzMDMxNTIzNjk1LnBuZw==.png)
