【发布时间】:2015-05-01 21:53:47
【问题描述】:
我正在创建一个工作簿,它将根据列中的值将数据从源工作表复制并粘贴到多个其他工作表。但是,一旦我启动宏,Excel 就会进入无响应状态。我在 4000 到 500,000 行的任何地方进行操作,但只有 4 列。当我只有约 4000 行时,它的运行速度非常快(3 秒)。当我有约 30,000 行时,Excel 进入无响应状态约 10 秒,但随后完成。 300,000 行测试我没有等待足够长的时间。
我的想法是根据B 列中的字符串对所有数据进行排序,将所有B 列(包含我正在搜索的字符串)放入一个数组中,然后拉所有唯一的字符串输出到另一个数组中。例如,如果列B 在第 1-200 行中包含“搜索”,在第 201-500 行中包含“创建”,则宏将搜索行,第二个数组(我们称之为场景)最终将包含两个值,“搜索”和“创建”。
在搜索过程中,我还创建了两个与场景数组对应的并行数组,该数组将保存该场景的开始行和结束行。之后,我将遍历并行数组中的值,然后从源工作表复制/粘贴到其他工作表。
注意:排序工作正常
有没有办法让它更快?
代码如下: 分配数据
Sub AllocateData()
Dim scenarioRange As String 'To hold the composite range
Dim parallelScenarioName() As String 'Holds the unique scenario names
Dim parallelScenarioStart() As Long 'Holds the starting row of the scenario
Dim parallelScenarioEnd() As Long 'Holds the ending row of the scenario
Sheets("raw").Activate 'Raw is the source worksheet
'Populates the parallel scenario arrays
Call GetScenarioList(parallelScenarioName, parallelScenarioStart, parallelScenarioEnd)
'Loops through the scenario parallel array and coes the copy and paste to other worksheets
'Workseets are named the same as the scenarios
For intPosition = LBound(parallelScenarioName) To (UBound(parallelScenarioName) - 1)
scenarioRange = "A" & parallelScenarioStart(intPosition) & ":" & "D" & parallelScenarioEnd(intPosition)
Range(scenarioRange).Select
Selection.Copy
Worksheets(parallelScenarioName(intPosition)).Activate
Range("A1").Select
ActiveSheet.Paste
Sheets("raw").Activate
Next
End Sub
获取场景列表
Sub GetScenarioList(ByRef parallelScenarioName() As String, ByRef parallelScenarioStart() As Long, ByRef parallelScenarioEnd() As Long)
Dim scenarioName As Variant
Dim TotalRows As Long
Dim arraySize As Long
arraySize = 1
'Prep the parallel array for scenario name with the first value
ReDim parallelScenarioStart(1)
ReDim parallelScenarioName(1)
parallelScenarioStart(0) = 1 'First spot on the scenario start will be row 1
'Prep the first scenario name
'Sometimes a number will be attached on the end of the scenario name delimited by a period. Ignore it.
If (InStr(Cells(1, 2).Text, ".") <> 0) Then
parallelScenarioName(0) = Left(Cells(1, 2).Text, InStr(Cells(1, 2).Text, ".") - 1)
Else
parallelScenarioName(0) = Cells(1, 2).Text
End If
'Get the total amount of rows
TotalRows = Rows(Rows.Count).End(xlUp).row
'Loop through all of the rows
For i = 1 To TotalRows
'Sometimes a number will be attached on the end of the scenario name delimited by a period. Ignore it.
If (InStr(Cells(i, 2).Text, ".") <> 0) Then
scenarioName = Left(Cells(i, 2).Text, InStr(Cells(i, 2).Text, ".") - 1)
Else
scenarioName = Cells(i, 2).Text
End If
'If the scenario name is not contained in the unique array
If IsNotInArray(scenarioName, parallelScenarioName) Then
Call AddScenarioEndRow(i, arraySize, parallelScenarioEnd)
Call AddNewScenarioToParallelArray(scenarioName, arraySize, parallelScenarioName)
Call AddNewScenarioStartRow(i, arraySize, parallelScenarioStart)
End If
Next
'Cleanup. The above code did not cover the ending row of the last scenario
Call AddScenarioEndRow(TotalRows + 1, arraySize, parallelScenarioEnd)
End Sub
IsNotInArray
Function IsNotInArray(stringToBeFound As Variant, ByRef parallelScenarioName() As String) As Boolean
IsNotInArray = Not (UBound(Filter(parallelScenarioName, stringToBeFound)) > -1)
End Function
并行数组
Sub AddNewScenarioToParallelArray(str As Variant, arraySize As Long, ByRef parallelScenarioName() As String)
arraySize = UBound(parallelScenarioName) + 1
ReDim Preserve parallelScenarioName(arraySize)
parallelScenarioName(arraySize - 1) = str
End Sub
Sub AddScenarioEndRow(row As Variant, ByRef arraySize As Long, ByRef parallelScenarioEnd() As Long)
ReDim Preserve parallelScenarioEnd(arraySize)
parallelScenarioEnd(arraySize - 1) = row - 1
End Sub
Sub AddNewScenarioStartRow(row As Variant, ByRef arraySize As Long, ByRef parallelScenarioStart() As Long)
ReDim Preserve parallelScenarioStart(arraySize)
parallelScenarioStart(arraySize - 1) = row
End Sub
【问题讨论】:
-
至少告诉我你为什么选择投反对票。如果人们知道原因,通常会帮助他们改进......
-
除非您打算对这些数组做其他事情,否则只需使用单个子对 ColB 进行预排序,您的代码就可以大大简化。
-
我实际上正在这样做。 ColB 在到达分配数据之前按 A-Z 排序
-
如何将此问题标记为已过时?我们最终想要一个工作表中的元数据,它给了我要从中提取的场景列表,而不是必须找到它们,然后我就可以只做一个 .Find(string)。我现在可以在几秒钟内完成约 500k 行。或者,发布新代码并将其标记为已解决是否更合适?
-
您可以发布您的“解决方案”作为答案并接受它。