簡體   English   中英

Excel VBA:在大型循環中比較日期的有效方法

[英]Excel VBA: Efficient way to compare dates in large loops

節目說明

我正在嘗試使用Sub來從許多文件中生成Raport。 每個文件都由按日期排序的行組成,其中沒有固定數目的行。 有的只有300個,有的超過10000。每一行都按模塊划分,描述了一些類似的問題,每個模塊中也有所有列的總和。 Raport應當在用戶設置的某些文件中顯示在用戶設置的時間內特定模塊出現了多少問題。

問題

我的Sub可以正常工作,但是我不確定我是否做得正確。 一個文件的操作大約需要6秒,但有時大約要2分鍾(最大文件循環中每個文件5000個循環)的時間很長。 我幾乎可以肯定,有更有效的方法來完成這項工作。 我想,主要問題是我檢查每一行中日期的方式-這也是最長的循環。 閱讀后:

我不太了解如何在此處應用過濾器查找功能,我也在嘗試使用ArraysForeach ,但是時間執行幾乎是相同的(有時更好,有時不是)。 我也認為許多If和嵌套循環可能會使Sub變慢。 也許在Excel VBA中有一些並行循環線程使用來加速它? 我認為Excel總是只使用處理程序的25%。 同樣,我嘗試給用戶一點機會來配置循環作用域( 代碼中的 Number1Number2 ),通過良好的設置將時間從2分鍾減少到30秒,但是需要不時檢查和清理DataBase文件,因此這不是最好的解決方案。

我剛剛開始編程,這是我的第一個大項目,所以我意識到代碼質量很差,希望您能為我提供一些指導,以使這只烏龜更快。 抱歉,很長的帖子。

它很大,所以我刪除了一些不太重要的和平(已描述)。

Sub CopyInfo()
   Application.ScreenUpdating = False
   Application.Calculation = xlCalculationManual
   Application.EnableEvents = False
   Sheets("Silnik").Select

   'Cleaning cells for raporting (they need to be empty)
    Call Czyść

   'Variable for storing data value
    Dim Value

   'Timer - to see how long it takes
    Dim t As Single
    t = Timer

   'Variables for opening and closing scope of checking data (editable by the user)
    Dim Data1, Data2
    Data1 = Cells(3, 9).Value
    Data2 = Cells(4, 9).Value

   'Position of cells in raport can change (P - row, P2 - column), easy edit
    Dim Postion, Position2
    Position = 9
    Position2 = 13

   'With row should I start looking (N1)? How many rows should I look for dates (N2)?
   'Get search scope values from sheet (these cells are editable by the user)
    Dim Number1, Number2
    Number1 = ActiveWorkbook.Sheets("Silnik").Cells(2, 28)
    Number2 = ActiveWorkbook.Sheets("Silnik").Cells(3, 28)

   'With files should I test? Do I need to test all of them, or just few (LiniaStany)
   'Check state of the file (user can edit with file hes testing)
   'Also - get names of the files (LiniaNazwy) - they can change in time
    Dim LiniaStany(16), LiniaNazwy(16)
    For i = 0 To 15
       LiniaStany(i) = ActiveWorkbook.Sheets("Silnik").Cells(2 + i, 22)
       LiniaNazwy(i) = ActiveWorkbook.Sheets("Silnik").Cells(2 + i, 21)
    Next

   'Variables for workbooks
    Dim wb As Workbook, wb2 As Workbook
    Dim vFile As Variant
   'Set current workbook (to this file)
    Set wb = ActiveWorkbook


   'Core
   'i means currently opened file
    For i = 0 To 15
       'Check if file should be tested, if yes, then set FilePath and open
       If (LiniaStany(i) > 0) Then
          vFile = "C:\Users\Kris\Desktop\kontrol " & LiniaNazwy(i) & " M.xlsm"
          Workbooks.Open vFile
         'Set DataBase workbook
          Set wb2 = ActiveWorkbook
         'Number is currently tested row in chosen file
          For Number = Number1 To Number2
             Value = wb2.Worksheets("Baza").Cells(6 + Number, 1)
             'Check if date is in the scope
             If (Value >= Data1) And (Value <= Data2) Then
                'Get information about SUM of problems in "module1"
                 wb.Sheets("Wyniki").Cells(Position - 1, 4 + i * 3) = wb.Sheets("Wyniki").Cells(Position - 1, 4 + i * 3) + wb2.Worksheets("Baza").Cells(6 + Number, 80)
                 'Check if problems>0, if yes, get more informations
                  If (wb2.Worksheets("Baza").Cells(6 + Number, 80).Value > 0) Then
                      For WK = 0 To 17
                          wb.Sheets("Wyniki").Cells(Position + WK, 4 + i * 3).Value = wb.Sheets("Wyniki").Cells(Position + WK, 4 + i * 3).Value + wb2.Worksheets("Baza").Cells(6 + Number, Position2 + WK).Value
                      Next WK
                  End If
                 'Get information about SUM of problems in "module2"
                  wb.Sheets("Wyniki").Cells(Position + 18, 4 + i * 3) = wb.Sheets("Wyniki").Cells(Position + 18, 4 + i * 3) + wb2.Worksheets("Baza").Cells(6 + Number, 82)
                  If (wb2.Worksheets("Baza").Cells(6 + Number, 82).Value > 0) Then
                      For ZAP = 0 To 9
                         'ZAP - Detale
                          wb.Sheets("Wyniki").Cells(Position + ZAP + 18, 4 + i * 3).Value = wb.Sheets("Wyniki").Cells(Position + ZAP + 18, 4 + i * 3).Value + wb2.Worksheets("Baza").Cells(6 + Number, Position2 + ZAP + 17).Value
                      Next ZAP
                  End If
                  'Some more ifs (7)..., same way, cut out
                  '...
                  '...
            End If
            'See if row is empty or not - if yes, stop the main loop
            If (Value < 1) Then
                Exit For
            End If
        Next Number
        'Close DataBase workbook, go to another one
        wb2.Close False
    End If
Next

Sheets("Raport").Select
Application.ScreenUpdating = screenUpdateState
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = eventsState

Beep
MsgBox "Operation time: " & Timer - t & " seconds."

End Sub

正如我在一段時間后發現的那樣,不需要像這樣循環比較日期。 決不。 Excel提供了篩選器,這些篩選器只能用於將滿足某些條件的單元格范圍縮小到篩選器中所述的條件。 最簡單的方法是打開宏記錄器並在單元格范圍內設置過濾器。 代碼應如下所示(在注入dateStart和dateEnd之后):

With Sheet1

            .AutoFilterMode = False

            .Range("A1:D1").AutoFilter

            .Range("A1:D1").AutoFilter Field:=2, Criteria1:=">=dateStart", _

             Operator:=xlAnd, Criteria2:="<=dateEnd"

    End With

但是,如果我們使用此過濾器遍歷范圍,則仍然可以獲得相同的結果。 為了提高效率,我們只需要使用可見(已過濾)的單元格即可。 為此,我們可以使用特殊的單元格

Set rng = Range("A2:D50")

For Each cl In rng.SpecialCells(xlCellTypeVisible)
    'Do something on cells in date range
Next cl

用此方法替換外循環后,可以從不同的列中過濾掉其他選項(添加具有不同字段和條件的不同過濾器)。 這樣,根本就不需要使用for循環 使用這種方法可以將時間從幾分鍾減少到幾秒鍾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM