Excel VBA中寫SQL,這些問題的方法你一定要牢記


  小爬之前的文章 【Excel VBA中寫SQL,這些問題你一定為此頭痛過】中詳細討論了一些常見的VBA 中使用SQL遇到的問題,這里再補充兩個常見的問題場景及對應的解決方案,希望你們看了后能夠思路開闊些,少走些彎路。

一、數據源Excel文件的工作表有些列的列名相同

  我們知道,在商業數據庫中創建表時,同一個表名下是不允許創建相同的字段的,且字段名要遵從一定規范。但在Excel文件中則無此限制,同一個工作表下,支持多列的列名相同。那么當我們用SQL來操作這類頭疼的Excel文件時,該如何區分不同字段呢?

  假設小爬有這樣一個Excel表(數據樣本通過python的第三方庫faker來生成),【源數據】表中,A列和C列的列名都為【公司】,但其實一個指代【公司名稱】,另一個指代【公司代碼】,這樣不規范的excel模板例子在現實工作中很常見。我們該如何區分它倆呢?

 

   小爬試着先輸出recordSet中所有字段名,看VBA的sql引擎是如何貼心處理這個問題的,示例代碼如下:

 1 Sub myQuery()
 2   Dim conn As Object, rs As Object, rs1 As Object, sht1 As Worksheet, sht2 As Worksheet, sql As String
 3   Set conn = CreateObject("ADODB.Connection")
 4   Set rs = CreateObject("ADODB.recordset")
 5   Set sht1 = ThisWorkbook.Sheets("源數據")
 6   Set sht2 = ThisWorkbook.Sheets("結果")
 7   conn.Open "provider=Microsoft.ACE.OLEDB.12.0;extended properties=excel 12.0;data source=" & ThisWorkbook.FullName
 8     sql = "SELECT * FROM [源數據$]"
 9   Set rs = conn.Execute(sql)
10   For i = 0 To rs.Fields.Count - 1 '輸出recordset字段名到【結果】表
11     sht2.Cells(1, i + 1) = rs.Fields(i).Name
12   Next
13   sht2.Cells(2, 1).CopyFromRecordset rs '輸出recordset結果到【結果】表
14   conn.Close
15   Set conn = Nothing
16 
17 End Sub

  輸出的結果如下圖所示:

 

   可以看到,重名后的列名被sql解析成字段名后,默認跟上阿拉伯數字1,2,3……知道了sql 引擎的解析規則,我們就可以直接根據解析后的列名 如【公司】【公司1】來操作不同的字段了,沒輸出所有字段名前就可以做到心中有數。

二、Excel“表格”不是真正的表格文件格式

  有的時候,我們從ERP系統導出的報表Excel文件,雖然是xls(xlsx)后綴,可出於種種原因,他們並非真正的Excel表格格式,可能底層依然是txt文件,小爬在工作中就沒少遇到過這種奇葩問題。

  解決思路如下:先利用sql的方法獲取當前數據庫的所有表名,如果表名是亂碼,如("?????"),則該Excel文件可能底層是txt文件,至少不是規范格式的Excel文件。此時,我們可以用VBA原生的workbooks.open方法來顯式打開該工作簿,自動保存,然后用SQL引擎來重新連接該工作簿即可。示例代碼如下:

 

Const adSchemaTables = 20 '這句很重要,一定要提前定義adSchemaTables常量的值
Sub myQuery()
  Dim conn As Object, rs As Object, rs1 As Object, sht1 As Worksheet, sht2 As Worksheet, sql As String, sourceFileName As String

  Set conn = CreateObject("ADODB.Connection")
  Set rs = CreateObject("ADODB.recordset")
  sourceFileName = ThisWorkbook.Path & "\數據源\" & "測試.xls"
  conn.Open "provider=Microsoft.ACE.OLEDB.12.0;extended properties=excel 12.0;data source=" & sourceFileName
    Set rs = conn.openschema(adSchemaTables)
    TableName = rs.Fields(2).Value
    If TableName <> "源數據" Then '假定當工作簿格式規范時,工作表名為【源數據】
        conn.Close
        Set wb = Workbooks.Open(sourceFileName)
        wb.Save
        wb.Close
        conn.Open "provider=Microsoft.Ace.oledb.12.0;Extended Properties=Excel 12.0;data source=" & sourceFileName
        Set rs = conn.openschema(adSchemaTables)
        TableName = rs.Fields(2).Value
    End If
End Sub

 

  這里面包含兩個技巧:

    1、當小爬用wb.save時,Excel會自動將不規范的xls文件(本質是txt)保存為規范的xls文件;

    2、利用conn.openschema(adSchemaTables)輸出該數據庫下所有的表名,代碼如下:

CONN.Open "provider=Microsoft.Ace.oledb.12.0;Extended Properties=Excel 12.0;data source=" & sourceFullName
Set rs = CONN.openschema(adSchemaTables)
Do While Not rs.EOF
    tableName = rs.Fields(2).Value

    Debug.Print rs.Fields(2).Value      '表名,對於Excel中的表或(工作表名)后面會自動加一個$
    rs.MoveNext
Loop

  至於上面的例子中,為啥不每次默認用VBA語法打開某個工作簿,再保存為xls文件,再用CONN來連接,自然是為了改善腳本的性能,畢竟workbooks.open相比較於CONN來連接表格,速度太慢了。

  

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM