好的,堆棧溢出,據我所知,這是一個乏味的。當在Excel中保存時,緩慢增加保存時間
我已經創建了一個宏啓用Excel文件,該運行時,執行以下操作(高電平):通過文件對話框
- 用戶選擇模板文件(其本身是宏啓用)通過文件對話框選擇數據文件(不啓用宏)
- 宏步驟通過數據文件並逐個打開它們,格式化數據,將數據遷移到中間工作簿中的新工作表中,然後關閉數據文件不保存
- 一旦所有文件都被循環使用,中間工作簿也會被保存,但保持打開狀態
- 一旦所有數據文件都被循環使用,每個中間工作簿都被循環,當前工作表中的數據被傳送到模板文件,並將模板文件保存爲一個新的,唯一標記的文件。在這種含現在數據文件中的一行數據被複制到一個彙總表
(這是比這更復雜一些,但這些都是很重要的方面,只要我可以告訴)
這是問題所在;正在選擇的數據文件的數量是成千上萬(到目前爲止,我們嘗試的最大運行量是4000個文件)。隨着宏的發展,這些文件保存所花費的時間變得越來越慢,而且越來越穩定。它在大約五秒鐘開始,但最後一些文件需要大約五分鐘才能保存。
我唯一的線索是有一個迭代功能,我補充說,一旦所有的數據文件都被循環了,它會完全關閉模板文件,並用不同的設置打開一個新的實例,然後再次啓動該過程。這會導致保存時間恢復正常,然後再次開始增長。摘要文件在此步驟中也會保存並關閉,併爲新的迭代打開一個新文件。
我已經考慮每隔百個數據文件關閉並重新打開模板文件,如果必須的話,我會執行該操作,但我寧願得到解決此問題的適當方法,而不是使用創可貼方法。如果我每次打開和關閉模板文件,我都會避免時間問題,但隨後宏變得非常不穩定,在運行期間它有時會完全隨機地崩潰(但有時會)。
這是在與互聯網或任何類型的網絡隔離的計算機上保存到固態驅動器(我們試圖控制相當多的變量)。
無論如何,我很難過,所以任何建議都歡迎!
Option Explicit
Public Sub Example()
Dim Trial As Integer, Trials As Integer, DataSet As Integer
Dim TrialChecker As Boolean
Dim StartTime As Double, WaitTime As Double
Dim StartDate As Date
Dim FileSaveName As String
Dim CopiedDataRange As Range
Dim SummaryRunTimes As Worksheet, Calcs As Worksheet, CutoffsShifts As Worksheet
Dim SheetObjects() As Worksheet
Dim IntermediaryWorkbook As Workbook, Summary As Workbook, Template As Workbook
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'The 1 and Trials are actually set by Lbound and Ubound funcitons, but the premise is the same
For Trial = 1 To Trials
Workbooks.Add
Set Summary = ActiveWorkbook
'I use this one sheet to keep track of how long different parts of the code take to run
Set SummaryRunTimes = Summary.Worksheets(1)
SummaryRunTimes.Name = "Run Times"
SummaryRunTimes.Cells(1, 1).Value = "ID"
SummaryRunTimes.Cells(1, 2).Value = "Data Copy Time (s)"
SummaryRunTimes.Cells(1, 3).Value = "Formula Copy and Calc Time (s)"
SummaryRunTimes.Cells(1, 4).Value = "Summary Copy Time (s)"
SummaryRunTimes.Cells(1, 5).Value = "Save and Cleanup Time (s)"
'sheetnames is defined elsewhere in the code (it's a global variable right now. I intend to change that later).
'It's simply an array of strings with six elements.
For Counter = LBound(sheetnames) To UBound(sheetnames)
Summary.Worksheets.Add
Summary.ActiveSheet.Name = sheetnames(Counter)
Next Counter
'Again, TemplateLocation is defined elsewhere. It's just a string grabbed from a filedialog
Workbooks.Open (TemplateLocation)
Set Template = ActiveWorkbook
Set Calcs = Template.Sheets("Calcs")
Set CutoffsShifts = Template.Sheets("Log Cutoffs & Shifts")
'SheetObjects is simply used as a convenient reference for various sheets in the template file. I found
'it cleaned up the code a bit. Some might say it's unnecessary.
For Counter = LBound(sheetnames) To UBound(sheetnames)
Set SheetObjects(Counter) = Template.Sheets(sheetnames(Counter))
Next Counter
'This is where the parameters for the given trial are set in the template file. Trialchecker is set elsewhere
'(it checks a yes/no dropdown in the original spreadsheet). ParameterAddresses is a range that's grabbed from a
'table object in the original spreadsheet and contains where these parameters go in the template file. These
'will not change depending on the trial, thus column = 1. TrialParameters is in the same table, and are the
'parameters themselves. These DO depend on the trial, and thus the column is equal to the trial number
If TrialChecker = True Then
For Counter = LBound(ParameterAddresses) To UBound(ParameterAddresses)
CutoffsShifts.Range(ParameterAddresses(Counter, 1)).Value = TrialParameters(Counter, Trial)
Next Counter
End If
For DataSet = 1 To IntermediaryWorkbook.Worksheets.Count - 1
'This is where I start my timers
StartTime = Timer
StartDate = Date
'This is where the data is actually copied from the intermediary file into the template. It's always five
'columns wide, but can be any number of rows. the SummaryRunTimes statement is merely grabbing the unique
'identifier of that given worksheet
With IntermediaryWorkbook
Set CopiedDataRange = Calcs.Range("$A$3:$E$" & .Worksheets(Counter).UsedRange.Rows.Count + 1)
CopiedDataRange.Value = IntermediaryWorkbook.Worksheets(Counter).Range("$A$2:$E$" & .Worksheets(Counter).UsedRange.Rows.Count).Value
SummaryRunTimes.Cells(Counter + 1, 1) = Calcs.Cells(3, 1).Value
End With
'First timestamp
SummaryRunTimes.Cells(Counter + 1, 2) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
StartTime = Timer
StartDate = Date
'This statement copies down the formulas that go with the data (which is aobut 100 columsn worth of formuals).
'Throughout this process, calculation is set to manual, so calculation is manually triggered here (Don't ask
'me why I do it twice. If I recall, it's because pivot tables are weird)
Set CopiedFormulaRange = Calcs.Range("$F$3:$KL$" & Calcs.UsedRange.Rows.Count)
CopiedFormulaRange.FillDown
Application.Calculate
Template.RefreshAll
Application.Calculate
'Second timestamp
SummaryRunTimes.Cells(Counter + 1, 3) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
StartTime = Timer
StartDate = Date
'This is a separate function that copies data from the template file into the summary sheet.
'I know you can't see the code, but just know that it only copies six sets of seven cells, so
'as far as I can tell, it's not what is causing the problem. The timestamp supports this idea, as
'it's consistent and short
Call SummaryPopulate(Summary, sheetnames, SheetObjects, r)
r = r + 1
'Third timestamp
SummaryRunTimes.Cells(Counter + 1, 4) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
StartTime = Timer
StartDate = Date
'These following few lines are meant to save the template file as a new file. As I mentioned, this is where
'things get bogged down. FileNameSuffix is a string set via a InputBox. TrialNames is set via the table object
'mentioned above, and is an array of strings.
Application.DisplayAlerts = False
If TrialChecker = True Then
FileSaveName = FolderLocation & "\" & Replace(Calcs.Cells(3, 1).Value, "/", " ") & " OOIP " & FileNameSuffix & " - " & TrialNames(1, Trial) & ".xlsm"
Else
FileSaveName = FolderLocation & "\" & Replace(Calcs.Cells(3, 1).Value, "/", " ") & " OOIP " & FileNameSuffix & ".xlsm"
End If
Template.SaveAs Filename:=FileSaveName, ConflictResolution:=xlLocalSessionChanges
Application.DisplayAlerts = True
'This part clears the copied data and formulas. I added the two Set Nothing lines in the hopes that it would
'solve my problem, but it doesn't seem to do anything
CopiedDataRange.ClearContents
CopiedDataRange.Offset(1, 0).Rows.Delete
Set CopiedDataRange = Nothing
Set CopiedFormulaRange = Nothing
'Fourth and final timestamp
SummaryRunTimes.Cells(Counter + 1, 5) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
'It seems to run a bit better if there's this Wait line here, but I'm not sure why. The WaitTime
'is grabbed from the original worksheet, and is a Double
Application.Wait (TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + WaitTime))
Next DataSet
'This but simply saves the summary file and then closes that and the template file. Then the process starts anew.
'This seems to be the key for resetting something that reduces the run times.
If TrialChecker = True Then
Summary.SaveAs Filename:=FolderLocation & "\" & "OOIP Summary " & FileNameSuffix & " - " & TrialNames(1, Trial) & ".xlsx"
Else
Summary.SaveAs Filename:=FolderLocation & "\" & "OOIP Summary " & FileNameSuffix & ".xlsx"
End If
Template.Close False
Summary.Close False
Next Trial
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
IntermediaryWorkbook.Close False
End Sub
在您的工作流程中,您沒有保存數據文件的步驟。您是否真的將數據文件中的數據複製到模板文件(一個模板文件,許多數據文件)?如果是這樣,唯一的保存是在每個數據文件導入後的模板文件。導入後關閉數據文件?也許你可以回顧一下你的問題,使其更清楚。我也認爲在沒有看到代碼的情況下做出任何事情都很難。 – Variatus
當你做這麼多文件時,內存是怎麼看的?你的任務管理器如何鎖定?是否有超過數百個Excel文件打開,或您的模板Excel增加他的記憶? – Moosli
@ Variatus你說得對,我很抱歉。我編輯了我的問題,嘗試提供更多細節。而且我可以包含我的代碼,但是如果可能的話,我寧願避免它。在這種情況下,我不得不削減它的意義。我可以說,在實現這一步之前,我可能會很有用,因爲我經常設置和重置範圍和工作表變量。我不確定這是否有所作爲,但我已經讀過它可能會讓內存變得雜亂無章(每次使用後我都試圖將它們設置爲Nothing),但這似乎不起作用)。 – apynn