2013-04-15 55 views
0

晚上好。危機:調和宏 - Excel 2010每次都掛起

兩個月前我開始在Excel 2010中開發一個宏,目的是協調兩組不同的信息。我在一個月前就擱置了這個項目,在這個時候,宏可以毫不費力地咀嚼每一行信息。

幾天前,我恢復了我在項目中的工作,並且實施了一些非常小規模的長陣列使用來包含滿足特定條件的行的位置。現在,這已經導致了讓Excel每次都掛在我頭髮上的經歷,我一次試圖運行宏。單步執行代碼可以毫無問題地完成,只要我不太快就行,但第二次讓它自行運行就會崩潰。狀態欄更新是主循環的一部分,它告訴我宏在管理器停止響應之前管理大約1%的行。

這是一個令人難以置信的令人沮喪的問題,因爲它 - 據我所知 - 根本不應該發生,並且必須歸因於在處理大量數據時強加給Excel的某些限制。也許它將我的循環解釋爲一個無限循環?

有一個覆蓋循環遍歷兩個數據集中的一個,它包含第二個循環,該循環遍歷第二個數據集的相對較小的部分以查找匹配。在崩潰發生之前,這個宏能夠處理大約是我現在使用的大小的11倍的數據集。將數據集的當前大小減少到上述默認值的10%仍然會導致宏使Excel掛起,但有趣的是它可以處理11%的數據。從中得出的明智結論是,數據集中某處存在一些實際的數據,這些數據導致Excel掛起,但1):如果出現這種情況,我會期待一條錯誤消息,並且2)檢查數據集在什麼構成1%導致沒有特別的發現。

所以我轉向你。我真誠地希望你能提出一些建議,以瞭解可能導致這種情況的原因以及我如何嘗試修復它。

這裏是有問題的子過程:http://pastebin.com/ywacHTVN

我一直在想,如果它拆分成幾個子過程會使它爲Excel更易消化,從而解決我的問題?如果是這種情況,如果有人能向我解釋原因,我將不勝感激。

我認爲應該提到的一件重要的事情是:之前我寫過這個宏,它能夠在沒有任何問題的情況下通過比當前數據集大11倍的數據集來處理數據集,然後再實現數組的小用法。但是這只是在我添加了常規執行之後 - 每次StatusBar更新時 - DoEvents;在這之前,Excel會像現在這樣掛起。

Sub MainRecon() 

Dim row_MSPS As Long, row_FPMS As Long, rowStart_FPMS As Long, rowEnd_FPMS As Long, row_FPMS_lastMatch As Long 
Dim row_midFPMS As Long, row_midMSPS As Long, IMO_Number As Long, size_MSPS As Long, row_MSPS_next As Long 
Dim n_matches As Integer, I_sup As Integer, temp_FPMS_Row As Long 

Dim match_Array() As Long 
Dim supreme_match_Array() As Long: ReDim supreme_match_Array(30) 
Dim IMO_FPMS_Pos_Array() As Long: ReDim IMO_FPMS_Pos_Array(30) 

Dim row_first_FPMS As Integer, I As Integer, IMO_matches As Integer, supreme_Size As Integer 

Dim order_no_FPMS As String 

Dim match As Boolean, quantity_MSPS As Boolean, IMO_next_match As Boolean, stock_update As Boolean 
Dim MSPS_duplicate As Boolean, FPMS_noMatches As Boolean, empty_FPMS As Boolean 

Dim deliveryDate_MSPS As Date, deliveryDate_FPMS As Date, deliveryDate_MSPS_next As Date 


row_MSPS = 2 
row_FPMS = 2 

row_midFPMS = 3 
row_midMSPS = 3 

size_MSPS = 2 

'Index for supreme match array. 
I_sup = 0 

Do While MSPS_RawWS.Cells(size_MSPS, 1) <> "" 
    size_MSPS = size_MSPS + 1 
Loop 


MainProcedure: 
Do While MSPS_RawWS.Cells(row_MSPS, 1) <> "" 'Stops at the end of the records 

    'Boolean variables defined 
    empty_FPMS = False 
    match = False 
    quantity_MSPS = False 
    IMO_next_match = False 
    stock_update = False 
    FPMS_noMatches = False 

    If IsNumeric(Left(MSPS_RawWS.Cells(row_MSPS, 7), 2)) = True _ 
     And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS, 7), 4, 2)) = True _ 
     And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS, 7), 7, 4)) = True Then 'Confirms date format DD-MM-YYYY of 'Time for Bunker' of MSPS 

     'Crew updated stock by reporting a new delivery instead of following proper procedure. 
     'Stock-Delivery difference smaller than 60 will be picked up as a stock update 
     'as well as delivery quantities under 10 [mt] 
     If ((60 > Abs(MSPS_RawWS.Cells(row_MSPS, 6) - MSPS_RawWS.Cells(row_MSPS, 8)) And _ 
      Abs(MSPS_RawWS.Cells(row_MSPS, 6) - MSPS_RawWS.Cells(row_MSPS, 8)) >= 0) Or (0 < MSPS_RawWS.Cells(row_MSPS, 8) And MSPS_RawWS.Cells(row_MSPS, 8) <= 10)) And _ 
      (MSPS_RawWS.Cells(row_MSPS, 6) + MSPS_RawWS.Cells(row_MSPS, 8) > 0) Then 

        MSPS_RawWS.Range("A" & row_MSPS, "H" & row_MSPS).Copy 
        mid_ReportWS.Cells(row_midMSPS, 11).PasteSpecial 

        mid_ReportWS.Cells(row_midMSPS, 9) = "Error 40. Updated stock reported as delivery." 

        row_midMSPS = row_midMSPS + 1 
        row_midFPMS = row_midFPMS + 1 

        Call UpdateProgress("", 4, row_MSPS, size_MSPS) 


     Else 'Proceed if it passes the stock update check 

      Call UpdateProgress("", 4, row_MSPS, size_MSPS) 

      quantity_MSPS = False 

      If MSPS_RawWS.Cells(row_MSPS, 8) > 0 Then 'If MSPS quantity is above 0, proceed 

      quantity_MSPS = True 

       If IsNumeric(Left(MSPS_RawWS.Cells(row_MSPS, 7), 2)) = True _ 
       And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS, 7), 4, 2)) = True _ 
       And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS, 7), 7, 4)) = True Then 'Confirms date format DD-MM-YYYY 

       deliveryDate_MSPS = Left(MSPS_RawWS.Cells(row_MSPS, 7), 10) 'Cuts away HH:MM:SS 
       IMO_Number = MSPS_RawWS.Cells(row_MSPS, 2) 

       'Finds the next MSPS record with quantity and date. 
       row_MSPS_next = row_MSPS + 1 
       Do While (MSPS_RawWS.Cells(row_MSPS_next, 7) = "" Or Not MSPS_RawWS.Cells(row_MSPS_next, 8) > 0) And row_MSPS_next <= size_MSPS _ 
       And Not (IsNumeric(Left(MSPS_RawWS.Cells(row_MSPS_next, 7), 2)) = True _ 
       And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS_next, 7), 4, 2)) = True _ 
       And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS_next, 7), 7, 4)) = True) 

        row_MSPS_next = row_MSPS_next + 1 

       Loop 


       'Checks if the next MSPS record has an IMO that matches the current one, and gets the date of the next record 
       IMO_next_match = False 
       If IMO_Number = MSPS_RawWS.Cells(row_MSPS_next, 2) And (IsNumeric(Left(MSPS_RawWS.Cells(row_MSPS_next, 7), 2)) = True _ 
       And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS_next, 7), 4, 2)) = True _ 
       And IsNumeric(Mid(MSPS_RawWS.Cells(row_MSPS_next, 7), 7, 4)) = True) And MSPS_RawWS.Cells(row_MSPS_next, 8) > 0 Then 

        deliveryDate_MSPS_next = Left(MSPS_RawWS.Cells(row_MSPS_next, 7), 10) 
        IMO_next_match = True 

       End If 

       'Checks if the MSPS record is a duplicate 
       If IMO_next_match = True And deliveryDate_MSPS = deliveryDate_MSPS_next And _ 
       MSPS_RawWS.Cells(row_MSPS, 8) = MSPS_RawWS.Cells(row_MSPS_next, 8) Then 

          MSPS_RawWS.Range("A" & row_MSPS, "H" & row_MSPS).Copy 
          mid_ReportWS.Cells(row_midMSPS, 11).Paste 

          mid_ReportWS.Cells(row_midMSPS, 9) = "Duplicate entry." 

          row_midMSPS = row_midMSPS + 1 
          row_midFPMS = row_midFPMS + 1 

          Call UpdateProgress("", 4, row_MSPS, size_MSPS) 

          row_MSPS = row_MSPS + 1 

          'Proceed prematurely to the next iteration in the all-encompassing 'Do While'-loop 
          'if the current MSPS-record is a duplicate 
          GoTo MainProcedure 
       End If 

       match = False 
       row_first_FPMS = 0 

        Do While IsEmpty(FPMS_RawWS.Cells(row_FPMS, 1)) = False And (IMO_Number > FPMS_RawWS.Cells(row_FPMS, 1) _ 
        Or IMO_Number = FPMS_RawWS.Cells(row_FPMS, 1)) 'Search for FPMS records with matching IMO number 

         If IMO_Number = FPMS_RawWS.Cells(row_FPMS, 1) Then 

          If row_first_FPMS > 0 Then 
           If FPMS_RawWS.Cells(row_first_FPMS, 1) <> FPMS_RawWS.Cells(row_FPMS, 1) Then 

            row_first_FPMS = row_FPMS 'This is the very first of the matching FPMS records 
            'For use later in connection with the arrays. 

           End If 

          Else 

           row_first_FPMS = row_FPMS 

          End If 


          If deliveryDate_MSPS = FPMS_RawWS.Cells(row_FPMS, 5) Or deliveryDate_MSPS = FPMS_RawWS.Cells(row_FPMS, 5) - 1 Or deliveryDate_MSPS = FPMS_RawWS.Cells(row_FPMS, 5) + 1 Then 

           match = True 

           Exit Do 

          End If 
         End If 

         row_FPMS = row_FPMS + 1 

        Loop 

       If match = True Then 

       'The following array will contain the location (row) of all FPMS records matching the current MSPS record 
       ReDim match_Array(30) 

       match_Array(0) = row_FPMS 
       n_matches = 1 

       row_FPMS_lastMatch = row_FPMS 
       order_no_FPMS = FPMS_RawWS.Cells(row_FPMS, 4) 

       rowStart_FPMS = row_FPMS 'Multiple entries can exist in FPMS for a single entry in MSPS. This is the lower boundary 

       row_FPMS = row_FPMS + 1 

       Do While IMO_Number = FPMS_RawWS.Cells(row_FPMS, 1) 

        'The FPMS order numbers are made up of 8 ciphers: XXXXXXXN 
        'The 7 first ciphers are used to tie orders together. MSPS usually has a single entry for all FPMS 
        'entries under XXXXXXX. 
        If Left(order_no_FPMS, 7) = Left(FPMS_RawWS.Cells(row_FPMS, 4), 7) And order_no_FPMS = FPMS_RawWS.Cells(row_FPMS, 4) Then 

         match_Array(n_matches) = row_FPMS 
         n_matches = n_matches + 1 

         row_FPMS = row_FPMS + 1 

        ElseIf deliveryDate_MSPS = FPMS_RawWS.Cells(row_FPMS, 5) Or deliveryDate_MSPS = FPMS_RawWS.Cells(row_FPMS, 5) - 1 Or deliveryDate_MSPS = FPMS_RawWS.Cells(row_FPMS, 5) + 1 Then 

         match_Array(n_matches) = row_FPMS 
         n_matches = n_matches + 1 

         row_FPMS = row_FPMS + 1 

         'If the next valid MSPS record is on the date after the current one, and the next FPMS record is as well, exit loop 
         If IMO_next_match = True And deliveryDate_MSPS_next = FPMS_RawWS.Cells(row_FPMS, 5) Then 

          Exit Do 

         End If 

        End If 
        Loop 

        'Upper boundary of range. 
        rowEnd_FPMS = row_FPMS - 1 

        If n_matches = 1 Then 

         FPMS_RawWS.Range("A" & match_Array(0), "H" & match_Array(0)).Copy 
         mid_ReportWS.Cells(row_midFPMS, 1).PasteSpecial 

         MSPS_RawWS.Range("A" & row_MSPS, "H" & row_MSPS).Copy 
         mid_ReportWS.Cells(row_midMSPS, 11).PasteSpecial 

        ElseIf n_matches > 1 Then 

         For I = 0 To n_matches - 1 

          FPMS_RawWS.Range("A" & match_Array(I), "H" & match_Array(I)).Copy 
          mid_ReportWS.Range("A" & row_midFPMS + I).PasteSpecial 

         Next I 

         MSPS_RawWS.Range("A" & row_MSPS, "H" & row_MSPS).Copy 
         mid_ReportWS.Range("K" & row_midMSPS).PasteSpecial 

        End If 

        'Next free rows in mid-report 
        row_midMSPS = row_midMSPS + n_matches 
        row_midFPMS = row_midFPMS + n_matches 

        'The supreme_match_Array contains the row-position of all FPMS records that have been matched with an MSPS partner 
        'Empty the contents of the match_Array into the supreme array. 
        'The match_Array is recycled for every MSPS record - not every IMO number. 

        I = 0 

        Do Until match_Array(I) = 0 

         supreme_match_Array(I_sup) = match_Array(I) 

         I_sup = I_sup + 1 
         I = I + 1 

        Loop 


        'When the next MSPS record has a different IMO number than the current one, check supreme_match_Array against IMO_FPMS_Pos_Array 
        'to find out which FPMS records have not been paired with their MSPS counterparties, and copy these to the mid-report. 
        If IMO_next_match = False Then 

         temp_FPMS_Row = row_first_FPMS 

         IMO_matches = 0 

         'Find position of all FPMS records with matching IMO, and save this 
         Do While IMO_Number = FPMS_RawWS.Cells(temp_FPMS_Row, 1) 

          IMO_matches = IMO_matches + 1 

          IMO_FPMS_Pos_Array(IMO_matches - 1) = temp_FPMS_Row 

          temp_FPMS_Row = temp_FPMS_Row + 1 

         Loop 

         supreme_Size = 0 

         Do While supreme_match_Array(supreme_Size) > 0 'Find size of array 

          supreme_Size = supreme_Size + 1 

         Loop 


         For I = 0 To IMO_matches - 1 

          For I_sup = 0 To supreme_Size - 1 

           If IMO_FPMS_Pos_Array(I) = supreme_match_Array(I_sup) Then 

            IMO_FPMS_Pos_Array(I) = 0 
            GoTo NextIteration_I 

           End If 

          Next I_sup 
NextIteration_I: 
         Next I 

         For I = 0 To IMO_matches - 1 

          If IMO_FPMS_Pos_Array(I) > 0 Then 

           FPMS_RawWS.Range("A" & IMO_FPMS_Pos_Array(I), "H" & IMO_FPMS_Pos_Array(I)).Copy 
           mid_ReportWS.Cells(row_midFPMS, 1).PasteSpecial 

           mid_ReportWS.Cells(row_midFPMS, 9).Hyperlinks.Add Anchor:=mid_ReportWS.Cells(row_midFPMS, 9), Address:="", SubAddress:= _ 
           "'MSPS Raw'!A" & row_MSPS & ":R" & row_MSPS, TextToDisplay:="FPMS missing MSPS counter." 


'        Cells(row_midFPMS, 9).Select 
'        ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:="", SubAddress:= _ 
'        "'MSPS Raw'!A" & row_MSPS & ":R" & row_MSPS, TextToDisplay:="FPMS missing MSPS counter." 

           row_midFPMS = row_midFPMS + 1 

           FPMS_noMatches = True 

          End If 

         Next I 

         If FPMS_noMatches = True Then 

          'Next free rows in mid-report 
          row_midMSPS = row_midFPMS 

          FPMS_noMatches = False 

         End If 

         'The supreme array should be purged since we are moving on to another IMO-number 
         ReDim supreme_match_Array(30) 
         I_sup = 0 

        End If 

      ElseIf quantity_MSPS = True Then 


         Sheets("MSPS Raw").Activate 
         Range("A" & row_MSPS, "H" & row_MSPS).Copy 
         Sheets("Mid-Report").Activate 
         Cells(row_midMSPS, 11).Select 
         ActiveSheet.Paste 

         'Cells(row_midMSPS, 9) = "MSPS missing partner." 

         Cells(row_midMSPS, 9).Select 
         ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:="", SubAddress:= _ 
         "'FPMS Raw'!A" & row_FPMS_lastMatch & ":R" & row_FPMS_lastMatch, TextToDisplay:="MSPS missing partner." 

         row_midMSPS = row_midMSPS + 1 
         row_midFPMS = row_midFPMS + 1 

         row_FPMS = row_FPMS_lastMatch + 1 


      End If 'Match check 
      End If 'Date check 
      End If 'Quantity > 0 check 
    End If 'Error 40: Stock Update 
    End If 'Date format check 

    row_MSPS = row_MSPS + 1 

Loop 

End Sub 

編輯:更改數據集的大小沒有任何區別。無論數據集總共包含6000多行,還是總共只有200行,它仍然只能在碰撞前調整5-7行。

+0

如果你打開任務管理器,有沒有高比例的進程?什麼頁面文件的使用?我擔心Excel只具有「可擴展性」限制。我猜你是從數據庫中加載一整套記錄(或者是一個報表運行數據庫),然後在Excel宏中處理它們?也可能是將處理過程移到數據庫中的時候了。 –

+0

當Excel掛起時,只有12%的CPU正在使用,並且有足夠多的物理和虛擬內存可用。 對於從數據庫導入數據,你是部分正確的,但直到生產時纔會這樣。就像現在我剛剛手動導入數據集一樣,所以沒有與數據庫交互,也沒有任何I/O事件正在進行。 我真的希望我沒有達到Excel的上限。數據集不超過1700x4和4500x6大。正如我所提到的,大幅度縮小數據集的大小完全沒有區別。 – KHH

+0

鑑於您對數據的懷疑,我建議您在代碼中放置一個「kamikaze」行,並停留在任意記錄編號處,並且不斷修改,直到您可以驗證它是導致問題的特定記錄。或者,它可能不會每次都掛在同一記錄上,這意味着它不是基於數據的。 –

回答

0

啊基督,我覺得很愚蠢。我不小心從Do While循環中刪除了這個步驟句。我很抱歉浪費你的時間。現在,我的代碼運行速度比以往任何時候都快,這要感謝Chris提出的變體陣列。