Performance 提高FOR-loop的性能

Performance 提高FOR-loop的性能,performance,excel,vba,Performance,Excel,Vba,我正在比较工作簿中的工作表。该工作簿有两张名为PRE和POST的工作表,每张工作表中有相同的19列。行数每天都不同,但在特定的一天,两张图纸的行数相同。宏将前工作表中的每一行与后工作表中的对应行进行比较,如果两个工作表中的行相同,则删除它们 我有一些通常建议的提高性能的方法,比如将屏幕更新设置为FALSE等 我想为下一个循环优化这两个 Dim RESULT As String iPRE = ActiveWorkbook.Worksheets("PRE").Range("A1", Workshe

我正在比较工作簿中的工作表。该工作簿有两张名为PRE和POST的工作表,每张工作表中有相同的19列。行数每天都不同,但在特定的一天,两张图纸的行数相同。宏将前工作表中的每一行与后工作表中的对应行进行比较,如果两个工作表中的行相同,则删除它们

我有一些通常建议的提高性能的方法,比如将屏幕更新设置为FALSE等

我想为下一个循环优化这两个

Dim RESULT As String

iPRE = ActiveWorkbook.Worksheets("PRE").Range("A1", Worksheets("PRE").Range("A1").End(xlDown)).Rows.Count
'MsgBox iPRE
iPOST = ActiveWorkbook.Worksheets("POST").Range("A1", Worksheets("POST").Range("A1").End(xlDown)).Rows.Count
'MsgBox iPOST

If iPRE <> iPOST Then
    MsgBox "The number of rows in PRE and POST sheets are not the same. The macro quits"
    Exit Sub

Else
    iRows = iPRE
End If

 'Optimize Performance

    Application.ScreenUpdating = False

    EventState = Application.EnableEvents
    Application.EnableEvents = False

    CalcState = Application.Calculation
    Application.Calculation = xlCalculationManual

    PageBreakState = ActiveSheet.DisplayPageBreaks
    ActiveSheet.DisplayPageBreaks = False

    For iCntr = iRows To 2 Step -1
        For y = 1 To 20
            If Worksheets("PRE").Cells(iCntr, y) <> Worksheets("POST").Cells(iCntr, y) Then
                RESULT = "DeleteN"
                Exit For
            Else
                RESULT = "DeleteY"
            End If
        Next y

        If RESULT = "DeleteY" Then
            Worksheets("PRE").Rows(iCntr).Delete
            Worksheets("POST").Rows(iCntr).Delete
        End If
    Next iCntr

    'Revert optmizing lines

    ActiveSheet.DisplayPageBreaks = PageBreakState
    Application.Calculation = CalcState
    Application.EnableEvents = EventState
    Application.ScreenUpdating = True

End Sub
Dim结果为字符串
iPRE=ActiveWorkbook.Worksheets(“PRE”).Range(“A1”),Worksheets(“PRE”).Range(“A1”).End(xlDown)).Rows.Count
'MsgBox iPRE
iPOST=active工作簿.Worksheets(“POST”).Range(“A1”,Worksheets(“POST”).Range(“A1”).End(xlDown)).Rows.Count
'MsgBox iPOST
如果我是iPOST那么
MsgBox“预工作表和后工作表中的行数不相同。宏退出”
出口接头
其他的
iRows=iPRE
如果结束
“优化性能
Application.ScreenUpdating=False
EventState=Application.EnableEvents
Application.EnableEvents=False
CalcState=应用程序计算
Application.Calculation=xlCalculationManual
PageBreakState=ActiveSheet.DisplayPageBreaks
ActiveSheet.DisplayPageBreaks=False
对于iCntr=iRows至2步骤-1
对于y=1到20
如果工作表(“前”)单元格(iCntr,y)工作表(“后”)单元格(iCntr,y),则
RESULT=“DeleteN”
退出
其他的
RESULT=“DeleteY”
如果结束
下一个y
如果RESULT=“DeleteY”,则
工作表(“预”)行(iCntr)。删除
工作表(“POST”)。行(iCntr)。删除
如果结束
下一个iCntr
'还原优化行
ActiveSheet.DisplayPageBreaks=PageBreakState
Application.Calculation=CalcState
Application.EnableEvents=EventState
Application.ScreenUpdating=True
端接头

也许您可以进行两次调整,尽管它们对性能的影响非常小:

“准备对工作表的引用
将工作表变暗为工作表
将工作表标题设置为工作表
Set WorksheetPRE=ActiveWorkbook.Worksheets(“PRE”)
Set WorksheetPOST=ActiveWorkbook.Worksheets(“POST”)
然后,在代码中,替换
ActiveWorkbook.Worksheets(“PRE”)
WorksheetPRE

我认为,当您停留在Excel中时,不可能进行其他重要的优化。请记住,Microsoft Excel主要是一个计算器,而不是一个数据表处理工具

如果我真的需要加速比较,那么我会采用以下方法之一:

  • 将Excel工作表链接到Microsoft Access as表格,并在Access中执行比较(最简单)

  • 如上所述,但不是链接表,而是导入它

  • 如上两种,但使用Microsoft SQL Server(Express版本是免费的)


对工作表单元格的任何引用都很慢。当你在一个循环中做这件事时,它会显著增加。最好的速度提升来自于限制这些工作表引用

一个好方法是复制变量数组中的数据,并循环这些数据,用要保留的数据构建一个新的变量数组。然后将新阵列一次性放置在旧阵列上

使用200000行、20列、50%文本、50%数字的测试数据集,删除170000行:这段代码在我的硬件上运行大约30秒

Sub Mine2()
    Dim T1 As Long, T2 As Long, T3 As Long

    Dim ResDelete As Boolean
    Dim iPRE As Long, iPOST As Long
    Dim EventState  As Boolean, CalcState As XlCalculation, PageBreakState As Boolean
    Dim iCntr As Long, y As Long, iRows As Long
    Dim rPre As Range, rPost As Range

    Dim PreDat As Variant, PostDat As Variant, PreDelDat As Variant, PostDelDat As Variant

    Dim n As Long
    Dim wsPre As Worksheet, wsPost As Worksheet

    Set wsPre = ActiveWorkbook.Worksheets("PRE")
    With wsPre
        Set rPre = .Range(.Cells(1, .Columns.Count).End(xlToLeft), .Cells(.Rows.Count, 1).End(xlUp))
        PreDat = rPre.Value
        iPRE = UBound(PreDat, 1)
        'MsgBox iPRE
    End With

    Set wsPost = ActiveWorkbook.Worksheets("POST")
    With wsPost
        Set rPost = .Range(.Cells(1, .Columns.Count).End(xlToLeft), .Cells(.Rows.Count, 1).End(xlUp))
        PostDat = rPost.Value
        iPOST = UBound(PostDat, 1)
        'MsgBox iPOST
    End With

    If iPRE <> iPOST Then
        MsgBox "The number of rows in PRE and POST sheets are not the same. The macro quits"
        Exit Sub
    End If
    iRows = iPRE


    ReDim PreDelDat(1 To UBound(PreDat, 1), 1 To UBound(PreDat, 2))
    ReDim PostDelDat(1 To UBound(PostDat, 1), 1 To UBound(PostDat, 2))
    n = 1
    On Error GoTo EH:
 'Optimize Performance

    Application.ScreenUpdating = False
    EventState = Application.EnableEvents
    Application.EnableEvents = False

    CalcState = Application.Calculation
    Application.Calculation = xlCalculationManual

    PageBreakState = ActiveSheet.DisplayPageBreaks
    ActiveSheet.DisplayPageBreaks = False


    T1 = GetTickCount
    For y = 1 To UBound(PreDat, 2)
        PreDelDat(1, y) = PreDat(1, y)
        PostDelDat(1, y) = PostDat(1, y)
    Next

    n = 2
    For iCntr = 2 To UBound(PreDat, 1)
        ResDelete = True
        For y = 1 To UBound(PreDat, 2)
            If PreDat(iCntr, y) <> PostDat(iCntr, y) Then
                ResDelete = False
                Exit For
            End If
        Next y

        If Not ResDelete Then
            For y = 1 To UBound(PreDat, 2)
                PreDelDat(n, y) = PreDat(iCntr, y)
                PostDelDat(n, y) = PostDat(iCntr, y)
            Next
            n = n + 1
        End If
    Next iCntr
    T2 = GetTickCount
    Debug.Print "Compare Done in:", T2 - T1
    Debug.Print "Rows to delete:", n - 1

    rPre = PreDelDat
    rPost = PostDelDat

    T3 = GetTickCount
    Debug.Print "Delete Done In:", T3 - T1
CleanUp:
    'Revert optmizing lines
    On Error Resume Next
    ActiveSheet.DisplayPageBreaks = PageBreakState
    Application.Calculation = CalcState
    Application.EnableEvents = EventState
    Application.ScreenUpdating = True
Exit Sub
EH:
    ' Handle Errors here
    Debug.Assert False
    Resume
    Err.Clear
    Resume CleanUp
End Sub

如果我可以把我的两分钱放进去,这是我的建议

我已经测试了原始代码(唯一的改动是y=1到10的
,而不是y=1到20的
),并将我的代码和两张10列(最初为500000)250000行的数据表进行对比。我之所以使用10而不是20,是因为我不知道列中的数据是什么,作为替代,我使用了随机值1或2

  • 对于10列,这意味着存在
    2^10=1024
    可能性
  • 对于20列,这意味着存在
    2^20=1048576
    可能性
由于我希望每个表中至少有几个相等的行,所以我选择了10列方案

为了给宏计时,我设置了一个定时器宏,它调用宏来比较和删除数据

为了能够比较结果,在启动Excel并打开具有完全相同数据的文件后,直接执行两个宏

我有

  • 避免了
    活动的所有实例
  • 最小化Excel和VBA之间的数据读取和写入,这是通过在二维数组中收集图纸上的所有数据,然后分析数组来实现的
  • 收集范围内要删除的行(每张1行),并删除循环外部要删除的所有行
代码 结果 我的代码-500000行数据。 包含500.000行和10列的数据表已在14,23秒内处理完毕,561行被发现相等,并已被删除

原始代码-500000行数据。 不幸的是,我的系统无法处理此任务,Excel停止工作。

我的代码-250000行数据。 在4.72秒内处理了250.000行10列的数据表,发现313行相等,并已删除

原始代码-250000行数据。 在14.07秒内处理了250.000行和10列的数据表,发现313行相等并已删除


删除行(逐行)速度较慢,请尝试使用
Union
并一次性删除所有行,例如,如果宏将删除1000行,则使用
Union
将快1000倍,但如果要删除1或2行,此方法将没有帮助。这与我的代码在功能上完全相同。谢谢你的测试。看来你是对的@chrisneilsen。
Sub Demo()
    Dim ResDelete As Boolean
    Dim iPRE As Long, iPOST As Long
    Dim EventState  As Boolean, CalcState As XlCalculation, PageBreakState As Boolean
    Dim iCntr As Long, y As Long, iRows As Long
    Dim rPreDelete As Range, rPostDelete As Range

    Dim PreDat As Variant, PostDat As Variant

    With ActiveWorkbook.Worksheets("PRE")
        PreDat = .Range(.Cells(1, 20), .Cells(.Rows.Count, 1).End(xlUp)).Value
        iPRE = UBound(PreDat, 1)
        'MsgBox iPRE
    End With

    With ActiveWorkbook.Worksheets("POST")
        PostDat = .Range(.Cells(1, 20), .Cells(.Rows.Count, 1).End(xlUp)).Value
        iPOST = UBound(PostDat, 1)
        'MsgBox iPOST
    End With

    If iPRE <> iPOST Then
        MsgBox "The number of rows in PRE and POST sheets are not the same. The macro quits"
        Exit Sub
    End If
    iRows = iPRE

    On Error GoTo EH:
 'Optimize Performance

    Application.ScreenUpdating = False
    EventState = Application.EnableEvents
    Application.EnableEvents = False

    CalcState = Application.Calculation
    Application.Calculation = xlCalculationManual

    PageBreakState = ActiveSheet.DisplayPageBreaks
    ActiveSheet.DisplayPageBreaks = False

    For iCntr = 2 To UBound(PreDat, 1)
        ResDelete = True
        For y = 1 To 20
            If PreDat(iCntr, y) <> PostDat(iCntr, y) Then
                ResDelete = False
                Exit For
            End If
        Next y

        If ResDelete Then
            If rPreDelete Is Nothing Then
                Set rPreDelete = Worksheets("PRE").Rows(iCntr)
                Set rPostDelete = Worksheets("POST").Rows(iCntr)
            Else
                Set rPreDelete = Application.Union(rPreDelete, Worksheets("PRE").Rows(iCntr))
                Set rPostDelete = Application.Union(rPostDelete, Worksheets("POST").Rows(iCntr))
            End If
        End If
    Next iCntr
    If Not rPreDelete Is Nothing Then
        rPreDelete.Delete
        rPostDelete.Delete
    End If

CleanUp:
    'Revert optmizing lines
    On Error Resume Next
    ActiveSheet.DisplayPageBreaks = PageBreakState
    Application.Calculation = CalcState
    Application.EnableEvents = EventState
    Application.ScreenUpdating = True
Exit Sub
EH:
    ' Handle Errors here

    Resume CleanUp
End Sub
Sub CompareAndDelete()
    Dim WsPre As Worksheet, WsPost As Worksheet
    Dim Row As Long, Column As Long
    Dim ArrPre() As Variant, ArrPost() As Variant
    Dim DeleteRow As Boolean
    Dim DeletePre As Range, DeletePost As Range

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

    With ThisWorkbook
        Set WsPre = .Worksheets("PRE")
        Set WsPost = .Worksheets("Post")
    End With

    ArrPre = WsPre.Range(WsPre.Cells(1, 1), WsPre.Cells(WsPre.Cells(WsPre.Rows.Count, 1).End(xlUp).Row, 20))
    ArrPost = WsPost.Range(WsPost.Cells(1, 1), WsPost.Cells(WsPost.Cells(WsPost.Rows.Count, 1).End(xlUp).Row, 20))

    If Not UBound(ArrPre, 1) = UBound(ArrPost, 1) Then
        MsgBox "Unequal number of rows in sheets PRE and POST. Exiting macro.", vbCritical, "Unequal sheets"
    Else

        For Row = 2 To UBound(ArrPre, 1)
            DeleteRow = True
            For Column = 1 To UBound(ArrPre, 2)
                If Not ArrPre(Row, Column) = ArrPost(Row, Column) Then
                    DeleteRow = False
                    Exit For
                End If
            Next Column

            If DeleteRow = True Then
                If DeletePre Is Nothing Then
                    Set DeletePre = WsPre.Rows(Row)
                    Set DeletePost = WsPost.Rows(Row)
                Else
                    Set DeletePre = Union(DeletePre, WsPre.Rows(Row))
                    Set DeletePost = Union(DeletePost, WsPost.Rows(Row))
                End If

            End If
        Next Row

        If Not DeletePre Is Nothing Then DeletePre.Delete
        If Not DeletePost Is Nothing Then DeletePost.Delete

    End If

    With Application
        .ScreenUpdating = True
        .Calculation = xlCalculationAutomatic
        .EnableEvents = True
    End With

End Sub