【问题标题】:Optimize this Code (Vlookup-like code)优化此代码(类似 Vlookup 的代码)
【发布时间】:2015-09-11 12:06:24
【问题描述】:

我有 2 个文件。用户运行宏时已经打开的第一个文件有 5 个工作表。每个工作表在不同位置包含一个“订单项”列。示例工作表看起来像这样

-Date Time Order-item Order-Quanity 
-1020 9:30 item533333 (blank)
-1020 7:30 item733333 (blank)
-1020 2:30 item333332 (blank)
-1020 6:30 item121242 (blank)

运行宏后,用户将选择一个文件打开,如下所示:

-Order-item Order-Quantity
-item121242 183
-item333332 515
-item533333 27
-item333332 761

然后,宏会遍历原始文件中的每个工作表。在每个工作表上,它会找到 order-item 列所在的位置,然后遍历列上的每个项目。它在用户选择的文件中搜索订单项(通常是 A 列)并查找数量(总是与订单项列相邻,在本例中为 B 列)

运行原始工作表后应如下所示:

-Date Time Order-item Order-Quanity
-1020 9:30 item533333 27
-1020 7:30 item733333 515 
-1020 2:30 item333332 761
-1020 6:30 item121242 183

我创建了一个宏来执行此操作,但由于两个文件都相当大(原始文件大约有 10,000 行,而用户打开的文件有多达 50,000 行),我的宏需要一些时间来执行。我意识到我可以简单地做一个 Vlookup,filldown,然后粘贴值,它会快得多;然而,这是一个更大的自动化宏的一部分,这是不可行的。是否有人可以提出任何改进建议以使我的代码运行得更高效或更快?如果是这样,请告诉我。谢谢!

Public Sub OpenFile()

Dim FilePath As Variant
Dim FileName As String
Dim CurrentWorkbook As String
Dim thisWB As Workbook
Dim openWB As Workbook
Dim sh As Worksheet
Dim lastRow As Long
Dim myRange As Range
Dim FoundCell As Range
Dim counter1 As Long
Dim counter2 As Long
Dim orderColumn As Long

Set thisWB = Application.ActiveWorkbook
CurrentWorkbook = Application.ActiveWorkbook.Name
FilePath = Application.GetOpenFilename(FileFilter:= _
            "Excel Workbook Files(*.xl*),*.xl*", MultiSelect:=False, Title:="Select File")
If Not FilePath = False Then
    FileName = FilePath
    Set openWB = Application.Workbooks.Open(FileName)
    FileName = Mid(FileName, InStrRev(FileName, "\") + 1, Len(FileName)) 'extracts filename from path+filename
Else
    MsgBox ("File not selected or selected file not valid")
    Exit Sub
End If
Application.Workbooks(FileName).Activate
'--------------------------------------------------------------------------------------------------
'--------------gets table range from input box.  Defailt is Row A,B--------------------------------
'--------------------------------------------------------------------------------------------------
Set myRange = Application.InputBox( _
    "Select Table Range.  First Column should be Order-item, Second Column should be Order Grade", _
    "Select Range", "$A:$B", , , , , 8)
On Error GoTo 0
'for every worksheet in currentworkbook, find how many rows there are.and find location of _
order-item. then go through each row in the order-item column and compare to column A(order-item) _
on the user selected workbook.  if match is found, place column B into order-item column+1
Application.ScreenUpdating = False
For Each sh In thisWB.Worksheets
    lastRow = LastRowUsed(sh)
    'Find Order Column
    Set FoundCell = sh.Rows(1).Find(what:="Order-Item", LookIn:=xlValues, _
    LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
    MatchCase:=False, SearchFormat:=False)
    If Not FoundCell Is Nothing Then
        orderColumn = FoundCell.Column
    Else
        MsgBox ("Couldn't find ""Order-Item"" in Header, exiting macro")
        Exit Sub
    End If

    For counter1 = lastRow To 1 Step -1
        For counter2 = myRange.Rows.Count To 1 Step -1
        If sh.Cells(counter1, orderColumn) = myRange.Cells(counter2, 1).Value Then
            sh.Cells(counter1, orderColumn + 1) = myRange.Cells(counter2, 2)
            Exit For
        End If
        Next
    Next
Next
Application.ScreenUpdating = True
End Sub

【问题讨论】:

  • 嘿,蒂姆,你能帮我解释一下这些台词吗? arr = d(tmp) For i = LBound(arr) To UBound(arr) arr(i).Value = rw.Cells(2).Value 接下来我想知道如何设置单元格值。 d(tmp) 还有什么作用?我无法在任何地方找到这本词典的用法。我遇到的每个示例在使用字典执行任何操作时都使用其中一个函数,例如 add、exists 等。感谢您迄今为止的所有帮助

标签: excel vba optimization vlookup


【解决方案1】:

你为什么不让你的 VBA 使用 Application.worksheetFunction.VLOOKUP ?

【讨论】:

    【解决方案2】:

    编辑:更新以处理重复的 ID。

    Sub Tester()
        UpdateFromSelection Workbooks("Book3").Sheets("Sheet1").Range("A1:B21")
    End Sub
    
    Sub UpdateFromSelection(myRange As Range)
        Dim d, rw As Range, tmp, c As Range, arr, i
    
        Set d = GetItemMap()
    
        If d Is Nothing Then Exit Sub
        Debug.Print d.Count
        If d.Count = 0 Then
            MsgBox "nothing found!"
            Exit Sub
        End If
    
        For Each rw In myRange.Rows
            tmp = rw.Cells(1).Value
            If Len(tmp) > 0 Then
            If d.exists(tmp) Then
                arr = d(tmp)
                For i = LBound(arr) To UBound(arr)
                    arr(i).Value = rw.Cells(2).Value
                Next i
            End If
            End If
        Next rw
    
    End Sub
    
    Function GetItemMap() As Object
    Dim dict As Object, ws As Worksheet
    Dim f As Range, lastRow As Long, tmp, arr, ub As Long
    
        Set dict = CreateObject("scripting.dictionary")
        For Each ws In ThisWorkbook.Worksheets
            Set f = ws.Rows(1).Find(what:="Order-Item", LookIn:=xlValues, _
                                    LookAt:=xlWhole)
            If Not f Is Nothing Then
                Set f = f.Offset(1, 0)
                lastRow = ws.Cells(Rows.Count, f.Column).End(xlUp).Row
                Do While f.Row <= lastRow
                    tmp = Trim(f.Value)
                    If Len(tmp) > 0 Then
                        If Not dict.exists(tmp) Then
                            dict.Add tmp, Array(f.Offset(0, 1))
                        Else
                            'can same item# exist > once?
                            arr = dict(tmp)
                            ub = UBound(arr) + 1
                            ReDim Preserve arr(0 To ub)
                            Set arr(ub) = f.Offset(0, 1)
                            dict(tmp) = arr
                        End If
                    End If
                    Set f = f.Offset(1, 0)
                Loop
            Else
                MsgBox ("Couldn't find 'Order-Item' in Header!")
                Exit Function
            End If
        Next ws
    
        Set GetItemMap = dict
    End Function
    

    【讨论】:

    • 嘿,谢谢,我会尝试一下,然后将结果回复给您。如果您不介意,您能否解释一下 scripting.dictionary 是什么或它的作用?是的,相同的 item# 可以在加载值的工作簿中多次存在(并且通常确实存在很多次)。在用户选择要搜索的工作表中,每个项目# 仅存在一次。
    • 我测试过了。它似乎工作得更快。它在 Set dict(tmp) = Application.Union(dict(tmp),f.Offset(0, 1)) 处崩溃,说无效使用联合。在注释掉包含上述内容的 else 块后,它适用于工作簿的第一页,但不适用于以下 4。我确实更改了几行以使其适用于我的工作簿。对于每个 ws 在 thisWB.Worksheets.I 中添加了一个名为 thisWB 的全局变量,并将其设置为运行代码的工作簿。在这样做之前,它总是会说“无法找到订单项”并退出宏。宏本身存储在personal.xlsb
    • 我所做的另一项更改是在“Tester”子例程中,我将用户在他们选择打开的工作簿上选择的范围传递给“UpdateFromSelection”子
    • 经过进一步测试:如果工作簿中只有一张工作表,您编写的代码就可以正常工作。如果我有超过 1 张纸,那么它会崩溃,说“对象'应用程序'的方法'联合'失败”运行时错误 1004。如果我从代码中删除该行,它会部分工作,但无法填写重复的订单项值.除了第一张表之外,它也无法填写任何表。
    • 脚本字典是一个对象,可让您存储与“键”相关的事物(常规变量或对象)。即使对于大量项目,基于键的查找也非常快(我已经使用它超过 200 万次,没有任何问题)。我猜Union 失败可能是因为在同一张纸上没有重复。我没有在这部分投入太多精力,因为不清楚是否会有任何重复。
    猜你喜欢
    • 1970-01-01
    • 2018-08-04
    • 2011-04-09
    • 1970-01-01
    • 2018-10-04
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多