如果在大型数据集上使用Excel VBA循环执行速度非常慢,然后崩溃

时间:2016-06-23 08:13:09

标签: excel vba performance excel-vba loops

我不是开发人员,但我在这里阅读了一下,以便能够理解其中的一些内容。这可能是我面临的一个简单问题,但我似乎无法弄明白。谢谢你帮我解决这个问题!

我在Google的帮助下写了一个简短的脚本,该脚本应该将CSV导出转换为可读格式。它应该做更多的事情,但我已经面临性能问题,只是为了让一些条目可读。

这是我到目前为止所拥有的:

Sub MagicButton_Click()

'Find the last non-empty cell in column A
Dim LastRow As Long
LastRow = Range("A" & Rows.Count).End(xlUp).Row


'Set Variables to work with the cell content
Dim CellContent As String
Dim CellContentArr As Variant


'Set looping variables
Dim i, j As Integer
Dim FirstRow As Integer
Dim FirstCol As Integer
Dim ActiveCol As Integer
Dim itm As Variant

FirstRow = 1
FirstCol = 2

Dim x, y As String





'Loop (1) through all rows
For i = FirstRow To LastRow
    'Save cell content to string
    CellContent = ActiveSheet.Cells(i, 1).Text

        'Split string into array
        CellContentArr = Split(CellContent, "{")
        'Reset column
        ActiveCol = FirstCol

        'Loop (2) through the array
        For Each itm In CellContentArr

            'Remove quotations and other symbols
            itm = Application.WorksheetFunction.Clean(itm)
            itm = Replace(itm, """", "")

            'This is the part that creates performance issues
            'For j = 1 To Len(itm)
            '    x = Mid(itm, j, 1)
            '    If x Like "[A-Z,a-z,0-9 :.-]" Then
            '        y = y & x
            '    End If
            'Next j

            'itm = y
            'y = ""


            'Write each item in array to an individual cells within the same row
            ActiveSheet.Cells(i, ActiveCol) = itm
            ActiveCol = ActiveCol + 1

        Next itm
    Next i
End Sub

当我测试~10行时,这整个脚本工作正常。当在整行的220行中使用它时,它变得没有响应并最终崩溃。

在脚本中,我评论了导致此性能问题的原因。我猜这是因为有三个循环。第三个循环遍历字符串中的每个字符以检查它是否是允许的char,然后保留或删除它。

我可以做些什么来提高性能,或者至少可以做到让Excel不会无响应?

旁注:它应该适用于Mac和Mac。视窗。我不知道RegEx是否会有更好的性能来过滤掉不需要的char,但我也不知道是否可以将它用于Mac和Mac。视窗。

3 个答案:

答案 0 :(得分:2)

已经给出的答案将是对您的代码的良好调整。但是,可能有更好的方法。

首先,将一个范围读入数组并操纵结果数组明显快于逐个单元格读取。

其次,如果您正在迭代数组中的每个字符并使用大括号检查特定项目发信号通知新列,那么您不能在一次迭代中完成所有操作。首先拆分和清理似乎有点多余。

总而言之,您的代码可以像这样简单:

Dim lastCell As Range
Dim v As Variant
Dim r As Long
Dim c As Long
Dim i As Integer
Dim output() As String
Dim b() As Byte

'Read the values into an array
With ThisWorkbook.Worksheets("Sheet1")
    Set lastCell = .Cells(.Rows.Count, "A").End(xlUp)
    v = .Range(.Cells(1, "A"), lastCell).Value2
End With

ReDim output(1 To UBound(v, 1), 1 To 1)

'Loop through the array rows and characters
For r = 1 To UBound(v, 1)
    c = 1
    'Convert item to byte array - just personal preference, you could iterate a string
    b = StrConv(v(r, 1), vbFromUnicode)
    For i = 0 To UBound(b)
        Select Case b(i)
            Case 45, 46, 58, 65 To 90, 97 To 122, 48 To 57 '-, :, ., A-Z, a-z, 0-9
                output(r, c) = output(r, c) & Chr(b(i))
            Case 123 '{
                'add a column and expand output array if necessary
                If Len(output(r, c)) > 0 Then
                    c = c + 1
                    If c > UBound(output, 2) Then
                        ReDim Preserve output(1 To UBound(v, 1), 1 To c)
                    End If
                End If
            Case Else
                'skip it
        End Select
    Next
Next

'Write item to worksheet
ThisWorkbook.Worksheets("Sheet1").Cells(1, "B") _
    .Resize(UBound(output, 1), UBound(output, 2)).Value = output

答案 1 :(得分:0)

三件事 - 你需要禁用screenupdating,你需要以更好的方式声明变量。不要像" Dim a,b,c,d,e作为整数",因为只有最后一个是整数,其他是变体。最后但同样重要的是,不要在VBA中使用Integer,但这不是你的问题。

这应该更快:

Sub MagicButton_Click()

'Find the last non-empty cell in column A
Dim LastRow As Long
LastRow = Range("A" & Rows.Count).End(xlUp).Row


'Set Variables to work with the cell content
Dim CellContent As String
Dim CellContentArr As Variant


'Set looping variables
Dim i  As   Long
dim j as    Long
Dim FirstRow As Long
Dim FirstCol As Long
Dim ActiveCol As Long
Dim itm As Variant

FirstRow = 1
FirstCol = 2

Dim x   as string
dim y   As String

call onstart
'Loop (1) through all rows
For i = FirstRow To LastRow
    'Save cell content to string
    CellContent = ActiveSheet.Cells(i, 1).Text

        'Split string into array
        CellContentArr = Split(CellContent, "{")
        'Reset column
        ActiveCol = FirstCol

        'Loop (2) through the array
        For Each itm In CellContentArr

            'Remove quotations and other symbols
            itm = Application.WorksheetFunction.Clean(itm)
            itm = Replace(itm, """", "")

            'This is the part that creates performance issues
            'For j = 1 To Len(itm)
            '    x = Mid(itm, j, 1)
            '    If x Like "[A-Z,a-z,0-9 :.-]" Then
            '        y = y & x
            '    End If
            'Next j

            'itm = y
            'y = ""


            'Write each item in array to an individual cells within the same row
            ActiveSheet.Cells(i, ActiveCol) = itm
            ActiveCol = ActiveCol + 1

        Next itm
    Next i

    call onend

End Sub


Public Sub OnStart()

    Application.AskToUpdateLinks = False
    Application.ScreenUpdating = False
    Application.Calculation = xlAutomatic
    Application.EnableEvents = False
    Application.DisplayAlerts = False

End Sub

Public Sub OnEnd()

    Application.DisplayAlerts = True
    Application.ScreenUpdating = True
    Application.EnableEvents = True
    Application.StatusBar = False
    Application.AskToUpdateLinks = True

End Sub

答案 2 :(得分:0)

任务列表

  • 将源范围复制到数组
  • 清理阵列
  • 将数组复制回源范围
  • 使用TextToColumns将数据拆分为多个列
Sub MagicButton_Click2()
    Dim arData
    Dim LastRow As Long, i As Integer
    Dim dataRange As Range
    LastRow = Range("A" & rowS.Count).End(xlUp).Row

    Set dataRange = Range(Cells(1, 1), Cells(LastRow, 1))
    arData = dataRange.value


    For i = 1 To UBound(arData)
        arData(i, 1) = AlphaNumericOnly(CStr(arData(i, 1)))
    Next

    dataRange.value = arData
    dataRange.TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, _
        TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
        Semicolon:=False, Comma:=False, Space:=False, Other:=True, OtherChar _
        :="{", TrailingMinusNumbers:=True
End Sub

' http://stackoverflow.com/questions/15723672/how-to-remove-all-non-alphanumeric-characters-from-a-string-except-period-and-sp
Function AlphaNumericOnly(strSource As String) As String
    Dim i As Integer
    Dim strResult As String

    For i = 1 To Len(strSource)
        Select Case Asc(Mid(strSource, i, 1))
            Case 48 To 57, 65 To 90, 97 To 123: 'include 32 if you want to include space I added 123 to include the {
                strResult = strResult & Mid(strSource, i, 1)
        End Select
    Next
    AlphaNumericOnly = strResult
End Function