2015-02-10 290 views
0

所以我在Execl VB中通過以下腳本遍歷行並刪除那些不包含特定關鍵字的行。VBA行操作執行時間太長

Sub Main() 
    RowsDeleted = 0 
    Keyword = "COLA" 
    For i = 2 to ActiveSheet.UsedRange.Rows.Count 
     If InStr(Cells(i, 1).Value, Keyword) = 0 Then 
      Rows(i).Delete 
      RowsDeleted = RowsDeleted + 1 
      i = i - 1 
     End If 
    Next i 
    MsgBox("Rows Deleted: " & RowsDeleted) 
End Sub 

問題是這個腳本需要很長時間才能執行(大約8分鐘〜73000個條目)。爲什麼是這樣,我將如何去改進它?

+0

看一看此答案爲[替換方法](http://stackoverflow.com/a/14245591/445425) – 2015-02-11 04:11:57

回答

-2

修改你的代碼看起來像這樣:

Sub Main() 
    On Error Goto ErrHandler 
    Application.ScreenUpdating = False 

    RowsDeleted = 0 
    Keyword = "COLA" 
    For i = ActiveSheet.UsedRange.Rows.Count to 2 
     If InStr(Cells(i, 1).Value, Keyword) = 0 Then 
      Rows(i).Delete 
      RowsDeleted = RowsDeleted + 1 
      ' i = i - 1 ' -- manually changing the loop counter is a bad idea 
     End If 
    Next i 
    MsgBox("Rows Deleted: " & RowsDeleted) 

EndSub: 
    Application.ScreenUpdating = True 
    exit sub 
ErrHandler: 
    ' Error handling here 
    resume EndSub 
End Sub 

是必需的錯誤處理程序,以確保ScreenUpdating被恢復,即使是在一個錯誤的情況。

+1

這將錯過的行。你必須倒退。 – 2015-02-11 03:38:19

+0

@DougGlancy:反轉循環非常簡單--OP的確在問性能。 – 2015-02-11 05:28:53

+0

您的回覆將有助於解決性能問題,但不能解決OP爲什麼性能不佳的問題。 – 2015-02-11 06:37:11

1

對其他答案沒有意義,但這隻會幫助排除故障。 你需要做的是去除代碼行

Rows(i).Delete 

內的(潛在的)長期運行循環是什麼原因造成的減速。

你需要將它重新寫這樣的...

Sub Main() 
    RowsDeleted = 0 
    Keyword = "COLA" 

    Dim rng As Excel.Range 
    Dim arr() As Variant 
    Dim str As String 
    arr = ActiveSheet.UsedRange 
    Dim R As Long 

    For R = 1 To UBound(arr, 1) ' First array dimension is rows. 
     If InStr(arr(R, 1), Keyword) = 0 Then 
      If str <> "" Then 
       str = "," & str 
      End If 
      str = str & arr(R, 1).Address 
     End If 
    Next R 

    Set rng = ActiveSheet.Range(str) 
    RowsDeleted = rng.Rows.Count 
    rng.Delete 

    MsgBox ("Rows Deleted: " & RowsDeleted) 

End Sub 
+0

這不適合我。我在'arr = ActiveSheet.UsedRange'上得到了「類型不匹配」錯誤。 – 2015-02-11 04:01:22

+0

正確的想法,但一些實現問題:串聯將','放在錯誤的地方(使用'str = str&「,」')。你需要刪除整行,所以使用'rng.EntireRow.Delete'。範圍引用字符串的長度是有限制的,因此對於非常大的數據集可能需要在循環中刪除幾個時間(測試'Len(str)'以決定何時執行刪除操作)。這裏的兩個關鍵想法(循環變體數組,並在一個步驟中執行實際刪除)將使_huge_性能發生變化。 – 2015-02-11 04:06:27

+0

@Doug只需要'arr = ActiveSheet.UsedRange.Value' – 2015-02-11 04:15:01

0

它需要年齡可能是由於您的單元格的公式是將要被刪除。

你應該做的是關閉自動計算並在刪除之前清除該行的內容。你也應該從下往上開始!

試試這個:

Sub Main() 
    Dim lMode As Long 

    ' Store initial state of Calculation mode 
    lMode = Application.Calculation 
    ' Change to Manual Calculation 
    Application.Calculation = xlCalculationManual 
    ' Disable screen update 
    Application.ScreenUpdating = False 

    RowsDeleted = 0 
    Keyword = "COLA" 

    ' Start from bottom up! 
    For i = ActiveSheet.UsedRange.Rows.Count To 2 Step -1 
     If InStr(Cells(i, 1).Value, Keyword) = 0 Then 
      Rows(i).ClearContents 
      Rows(i).Delete 
      RowsDeleted = RowsDeleted + 1 
     End If 
    Next i 

    ' Restore screenupdate and calculation mode 
    Application.ScreenUpdating = True 
    Application.Calculation = lMode 

    MsgBox ("Rows Deleted: " & RowsDeleted) 
End Sub 
+3

一些不錯的想法,但總體方法並不是最佳實踐。您建議的提示將會提供較小的性能提升。顛倒逐行刪除的循環顯然是最佳做法,但是我的觀點是,您不應該一開始就按行進行。 – 2015-02-11 02:30:56

+2

另外,您需要'For i'語句中的'Step - 1'。 – 2015-02-11 03:35:55

+0

感謝@DougGlancy,固定循環計數器,不夠小心。 – PatricK 2015-02-11 06:12:30

0

這裏可能是一些看, 它過濾柱一種用於細胞<>「可樂」,並清除它們 然後它排序列A因此在A列中空白單元格現在在底部 然後刪除空白行。 不知道OP的ws的設置,可能需要進行調整。

在我的示例表上,我使用了81,000行,當我運行代碼時大約需要5秒。

Sub SomeDeleteCode() 
    Dim Rws As Long, Rng As Range, nwRw As Long 

    Rws = Cells(Rows.Count, "A").End(xlUp).Row 

    Application.ScreenUpdating = 0 
    Application.Calculation = xlCalculateManual 

    Columns("A:A").AutoFilter Field:=1, Criteria1:="<>*Cola*" 
    Set Rng = Range(Cells(2, 1), Cells(Rws, 1)).SpecialCells(xlCellTypeVisible) 
    Rng.ClearContents 
    ActiveSheet.AutoFilterMode = 0 

    Columns(1).Sort Key1:=Range("A1"), Header:=xlYes 
    nwRw = Cells(Rows.Count, "A").End(xlUp).Row 

    Range(Range("B" & nwRw + 1), Range("B" & nwRw + 1).End(xlDown)).EntireRow.Delete 
    Application.Calculation = xlCalculationAutomatic 
End Sub