在Excel VBA中使用SQL到底優勢在哪兒 VBA如何實現篩選條件之“排除某些值”

  小爬在之前的博文中多次提到,可以在VBA中寫SQL來操作Excel文件,實現各類數據處理和分析需求。那麼,你可能有這樣的疑問:Excel原生的VBA,數據透視表,數據分析功能不夠嗎,爲啥一定要用SQL來實現呢?在Excel VBA中使用SQL到底優勢在哪兒?今天小爬就來帶大家好好捋一捋這些問題。

  首先,在VBA中寫SQL,這基本算VBA的高階功能,不是所有人都掌握,因此,上述的場景除了SQL,確實都有其它技術方案。其次,如果我們對別的技術運用的更熟練,我們完全可以用自己最擅長的技術棧來解決問題。以結果論來說,只要能解決實際問題的方案,都是好方案。

  不過,小爬這裏想討論的是,假如我們對VBA原生方法,數據透視表,SQL等知識都很熟悉的前提下,我們該如何選擇技術棧來解決實際問題呢,這個時候,SQL的優勢在於什麼?我認爲有以下幾點:

一、執行效率最高

跟VBA原生方法和Excel公式比較起來,SQL執行時速度要快很多。當表格數量超過一萬行時,這種優勢越發明顯。數據量越大,越適合用SQL來解決問題,越發高效;

二、代碼極爲簡潔

①、數據匹配場景:我們如果有多個字段要匹配時,原則上需要多個Vlookup或者VBA中建立多個字典,但是,如果我們用SQL的外連接,可能一行代碼足夠了;

②、數據分組聚合場景:如果我們在VBA中錄製宏生成數據透視表代碼,來分組聚合求最大值,EXCEL後臺會自動生成大量代碼,這些代碼可讀性極差,可如果我們使用SQL Group By,結合max,min,average等聚合函數,我們也只用一行SQL語句即可。

三、實現較爲簡單

  有一些特殊場景,使用原生VBA方法將極爲複雜,但是使用SQL可能就一行代碼。以我之前一篇博文VBA如何實現篩選條件之“排除某些值”舉例,這個場景,那篇文章用到了很大篇幅來使用純VBA技巧實現這樣一個看似很簡單的需求,單的一個“篩選——不包含某些值”的VBA場景,我們需要用到錄製宏功能,一維數組、二維數組功能、數組的轉置方法、字典的remove方法、字典鍵快速存入數組方法等。但是其實,如果我們使用SQL,需要用到的知識點就少多了。

  我們重新回顧下這個場景,【源數據】表含有【姓名】【通訊地址】【郵政編碼】三列,【例外清單】表中存儲着待排除在外的姓名,【結果】表要求返回【源數據】表中不包含【例外清單】姓名的所有數據。我們使用Python faker庫生成20000行數據,方便比對大樣本時使用不同方法效率上的差異。

 

 

   如果用SQL,本質上我們需要將【源數據】表左外連接(Left Join)【例外清單】表,基於【姓名】列,再加上where條件即可。

 

 

具體代碼如下:

 1 Sub myQuery()
 2   Dim conn As Object, rs As Object, sht1 As Worksheet, sht2 As Worksheet, sht3 As Worksheet, sql As String, startTime As Date, endTime As Date
 3   startTime = Timer
 4   Set conn = CreateObject("ADODB.Connection")
 5   Set rs = CreateObject("ADODB.recordset")
 6   Set sht1 = ThisWorkbook.Sheets("源數據")
 7   Set sht2 = ThisWorkbook.Sheets("例外清單")
 8   Set sht3 = ThisWorkbook.Sheets("結果")
 9   conn.Open "provider=Microsoft.ACE.OLEDB.12.0;extended properties=excel 12.0;data source=" & ThisWorkbook.FullName
10   sql = "SELECT a.* FROM [源數據$]a LEFT JOIN [例外清單$]b ON a.姓名=b.姓名 WHERE b.姓名 IS NULL"
11   Set rs = conn.Execute(sql)
12   For i = 0 To rs.Fields.Count - 1 '輸出recordset字段名到【結果】表
13     sht3.Cells(1, i + 1) = rs.Fields(i).Name
14   Next
15   sht3.Cells(2, 1).CopyFromRecordset rs '輸出recordset結果到【結果】表
16   conn.Close
17   Set conn = Nothing
18   endTime = Timer
19   sht3.Activate
20   MsgBox "累計運行" & (endTime - startTime) & ""
21 
22 End Sub

運行結果如下所示,耗時約0.63秒:

 

我們再回顧下使用VBA字典來實現這一需求的方法,代碼如下:

 1 Sub dictWay()
 2   Dim conn As Object, rs As Object, sht1 As Worksheet, sht2 As Worksheet, sht3 As Worksheet, sql As String, startTime As Date, endTime As Date, maxRow1 As Integer, myDic As Object, maxRow2 As Integer
 3   startTime = Timer
 4   Application.ScreenUpdating = False
 5   Set sht1 = ThisWorkbook.Sheets("源數據")
 6   Set sht2 = ThisWorkbook.Sheets("例外清單")
 7   Set sht3 = ThisWorkbook.Sheets("結果")
 8   Set myDic = CreateObject("scripting.dictionary")
 9   maxRow1 = sht1.Cells(Rows.Count, 1).End(xlUp).Row
10   maxRow2 = sht2.Cells(Rows.Count, 1).End(xlUp).Row
11   Dim i As Integer, j As Integer, k As Integer
12   For i = 2 To maxRow2
13     myDic.Add sht2.Cells(i, 1).Value, ""
14   Next
15   
16   k = 1
17   For i = 1 To maxRow1
18     If myDic.exists(sht1.Cells(i, 1).Value) = False Then
19         For j = 1 To 3
20             sht3.Cells(k, j).Value = sht1.Cells(i, j).Value
21         Next
22         k = k + 1
23     End If
24   
25   Next
26 
27   endTime = Timer
28   sht3.Activate
29   Application.ScreenUpdating = True
30   MsgBox "累計運行" & (endTime - startTime) & ""
31 
32 End Sub

爲了讓該方法儘可能提速,我們使用了字典,同時關掉了Excel的屏幕刷新。最終20000行數據時,耗時約4秒:

 

可以看到,與之前VBA的方法比起來,只需要一行SQL語句,邏輯清晰易懂,代碼簡潔明瞭,程序運行耗時是傳統方法的1/6不到,這就是小爬力推在VBA中使用SQL的主要原因~~

歡迎掃碼關注我的公衆號 獲取更多爬蟲、數據分析的知識!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章