【问题标题】:Excel VBA - A faster way to loop through a bunch of Range.FindsExcel VBA - 一种更快的方法来遍历一堆 Range.Finds
【发布时间】:2015-01-13 07:11:23
【问题描述】:

我正在执行“类似加入”的操作(类似于数据库),其中我有两个不同的数据集 A 和 B,它们有一些共同的标识符(我们称之为“id”)。我的目标是创建第三个数据集 C,它由 A 和 B 的交集组成(加入 id)。在 SQL 中,相应的查询类似于

SELECT a.id, a.some_column, b.another_column 
FROM a,b 
WHERE a.id = b.id 

我可以在 Excel 中执行相同的操作,方法是遍历 A 的 id 列中的所有行,并对 A 中的每一行在 B 的 id 列上执行 Range.Find。像这样:

For Each r in Worksheet_A.Range(Cells(start_row_a,id_column_a),Cells(end_row_A,id_column_a))
    Set found = Worksheet_B.Range(Cells(start_row_b,id_column_b),Cells(end_row_b,id_column_b)).Find(r.Value)
    If Not found is Nothing Then
        ' write stuff to Worksheet_C, e.g. found.Value, found.Offset(0,1).Value, r.Offset(0,-1).Value, etc.
    End If
Next r

这很好用,但速度很慢。我知道像 VB 这样的语言中的显式循环非常慢。我的问题是:有没有更快的路?我错过了一个更好的实现吗?我能在 SO 上找到的最接近的问题是 here,但我并不真正了解最佳答案,我不确定它是否适用于我的场景。

【问题讨论】:

    标签: performance vba excel


    【解决方案1】:

    您尝试使用ADO (ActiveX Data Object)
    这将允许您使用示例中的简单 SQL 语句。

    示例:

    Sub ject()
        Dim con As Object: Set con = CreateObject("ADODB.Connection")
        Dim rec As Object: Set rec = CreateObject("ADODB.Recordset")
        Dim datasource As String
        datasource = "C:\Users\user.name\Desktop\TestFolder\Test.xlsx" 'change to suit
    
        Dim sconnect As String
        'Connection string used is for Excel 2007 and up
        sconnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
                    "Data Source=" & datasource & ";" & _
                    "Extended Properties=""Excel 12.0;HDR=YES"";"
        con.Open sconnect
    
        Dim sqlstr As String
    
        sqlstr = "SELECT e.column1, u.column2, e.column3, e.column4 "
        sqlstr = sqlstr & "FROM [A$] e " 'from a sheet named A
        sqlstr = sqlstr & "INNER JOIN [B$] u " 'compare with a sheet named B
        sqlstr = sqlstr & "ON e.id = u.id " 
        sqlstr = sqlstr & "GROUP BY e.column1, u.column2, e.column3, e.column4 "
    
        rec.Open sqlstr, con, 3, 1
    
        Dim lrow As Long
        With Sheets("C") 'Change to suit
            .Range("A1").CopyFromRecordset rec
        End With
    
        rec.Close: con.Close
        Set rec = Nothing: Set con = Nothing
    End Sub
    

    这种方法对你来说可能有点新,但我确信它确实有效,而且速度肯定很快
    (虽然我没有真正比较过速度)。
    要了解 XL 2003 and belowXL 2007 and up 的连接字符串,请查看链接。
    顺便说一句,您可以将数据源设置到运行宏的同一工作簿中。

    示例:

    datasource = ThisWorkbook.FullName
    

    您有工作表 AB 以及另一个工作表 C 用于输出。 HTH。

    【讨论】:

      【解决方案2】:

      加速这类事情的最重要方法是知道“VBA 世界”和“工作表世界”之间的交互很慢,因此您应该将它们保持在最低限度。

      所以你应该像这样重构你的代码:

      1. 将整个 WorksheetA 表吞入一个数组中
      2. 将整个 WorksheetB 表吞入一个数组中
      3. 像现在一样做你的 for-cycle 工作,但在这些数组上,并写入第三个数组
      4. 将整个第三个数组放入工作表中

      这是使用此处记录的变体数组完成的:

      http://msdn.microsoft.com/en-us/library/ff726673.aspx#xlFasterVBA

      在标记为:在单个操作中读取和写入大块数据的部分中

      重构比看起来更容易,因为数组也采用行和列坐标,就像原始表格一样。

      当然,如果您的表太大,您可能会遇到内存问题,但以今天的 RAM 大小,它们必须非常非常大才会成为问题......

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-28
        • 2011-03-10
        • 2013-09-28
        • 2018-12-12
        • 2015-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多