【问题标题】:Is there a faster way to perform this VBA macro?有没有更快的方法来执行这个 VBA 宏?
【发布时间】:2015-03-19 12:04:12
【问题描述】:

我正在尝试将 Excel 中的多个工作表合并为 1 个工作表。我使用合并的工作表在同一个工作簿中运行一组报告。

  • 需要合并的工作表数量各不相同 - 大约 6 到 40 个工作表。我通过在它们前面加上“Src”来标识要合并的工作表
  • 在同一个工作簿中还有大约 40 个其他电子表格(根据合并工作表和其他计算得出的报告)
  • 要合并的每个工作表的格式相同

我是宏的新手,所以在互联网上找到了一些代码来自动整合,但是当我有 12 个左右的工作表需要整合时,它的运行速度非常慢。

无论如何我可以加快这个宏的速度吗?

Sub CopyDataWithoutHeaders()
Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim shLast As Long
Dim CopyRng As Range
Dim StartRow As Long

With Application
    .ScreenUpdating = False
    .EnableEvents = False
End With

' Clear the consol worksheet except the top row with the headings
Set DestSh = ActiveWorkbook.Worksheets("All Data")
DestSh.Rows("5:" & Rows.Count).ClearContents


' Fill in the start row.ie the row in each of the source sheets that contain the data (do not include heading rows)
StartRow = 5

' Loop through all worksheets and copy the data to the
' summary worksheet if worksheet name starts with src.
For Each sh In ActiveWorkbook.Worksheets
    If LCase(Left(sh.Name, 3)) = "src" Then

        ' Find the last row with data on the summary
        ' and source worksheets.
        Last = LastRow(DestSh)
        shLast = LastRow(sh)

        ' If source worksheet is not empty and if the last
        ' row >= StartRow, copy the range.
        If shLast > 0 And shLast >= StartRow Then
            'Set the range that you want to copy
            Set CopyRng = sh.Range(sh.Rows(StartRow), sh.Rows(shLast))

           ' Test to see whether there are enough rows in the summary
           ' worksheet to copy all the data.
            If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
               MsgBox "There are not enough rows in the " & _
               "summary worksheet to place the data."
               GoTo ExitTheSub
            End If

            ' This statement copies values, and formats.
            CopyRng.Copy
            With DestSh.Cells(Last + 1, "A")
                .PasteSpecial xlPasteValues
                .PasteSpecial xlPasteFormats
                Application.CutCopyMode = False
            End With

        End If

    End If
Next


ExitTheSub:
Application.GoTo DestSh.Cells(1)

' AutoFit the column width in the summary sheet.
DestSh.Columns.AutoFit

With Application
    .ScreenUpdating = True
    .EnableEvents = True
End With
End Sub

Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
                        After:=sh.Range("A1"), _
                        Lookat:=xlPart, _
                        LookIn:=xlFormulas, _
                        SearchOrder:=xlByRows, _
                        SearchDirection:=xlPrevious, _
                        MatchCase:=False).Row
On Error GoTo 0
End Function


Function LastCol(sh As Worksheet)
On Error Resume Next
LastCol = sh.Cells.Find(What:="*", _
                        After:=sh.Range("A1"), _
                        Lookat:=xlPart, _
                        LookIn:=xlFormulas, _
                        SearchOrder:=xlByColumns, _
                        SearchDirection:=xlPrevious, _
                        MatchCase:=False).Column
On Error GoTo 0
End Function

【问题讨论】:

  • 您可以关闭计算和屏幕更新。
  • 尝试切断一些像“StartRow”这样的声明,因为它的值是固定的,它不是一个变量,所以只要在她出现的地方放5,如果vlue没有沿着代码改变,那里不需要为它创建一个变量,它不会变得更快,但它确实有帮助
  • @YgorYansz 实际上,在较长的过程中,使用这样的常量或变量是有意义的 - 如果将来需要更新该值,那么您只需在此行上更改一次,而不是查找每行并替换值。
  • @SO 我知道,我的意思是在这个特定的情况下,因为它只出现两次并且它从不随代码改变,所以它只是针对这个特定的情况

标签: vba excel


【解决方案1】:

假设您有一列从头到尾没有空白行,请尝试将LastRow() 函数替换为

Function LastRow(sh As Worksheet)
Dim i as Long, R as Range

    i = 1
    Set R = sh.[A1]

    Do while R(i,1) <> ""    ' we asume column A (.. 1) here
        i = i+1
    Loop

    LastRow = i

End Function

或者,您可以使用.CurrentRegion 属性来避免将行和列一起计算。

【讨论】:

    【解决方案2】:

    替换:

    Last = LastRow(DestSh)
    shLast = LastRow(sh)
    

    Last = DestSh.UsedRange.Rows.Count
    shLast = sh.UsedRange.Rows.Count
    

    您有一个LastCol(sh) 函数,但我实际上并没有看到对它的调用。如果我错过了,您可以将函数调用替换为:

    sh.UsedRange.Columns.Count
    

    通常,粘贴会复制值和格式,因此,除非您特别想避免某些事情,否则请替换:

    With DestSh.Cells(Last + 1, "A")
      .PasteSpecial xlPasteValues
      .PasteSpecial xlPasteFormats
      Application.CutCopyMode = False
    End With
    

    DestSh.Cells(Last + 1, "A").Paste
    Application.CutCopyMode = False
    

    最后,请记住,VBA 代码在执行时被解释,并且本质上会比预编译代码慢。而且,它仍然比手工操作要快得多!

    【讨论】:

    • Last = DestSh.UsedRange.Rows.Count 假定 UsedRange 从第 1 行开始,您应该使用 With DestSh.UsedRange: Last = .Rows(.Rows.Count).Row: End With 之类的东西来获取使用范围内最后一行的实际行号。
    • @SO,这是真的,他的数据从StartRow = 5开始,但他在使用LastshLast时确实考虑到了这一点,所以不必担心与 this 案例相关。
    • 当然,没有看到实际数据,这一切都只是猜测,但我想我会提到它,以防其他人看到这个页面并在不同的数据集上使用这个答案。
    • 谢谢。我已经实施了所有这些建议,运行时间缩短到不到一分钟。
    猜你喜欢
    • 1970-01-01
    • 2015-02-10
    • 2015-11-29
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    相关资源
    最近更新 更多