【发布时间】:2015-06-26 19:05:28
【问题描述】:
我目前有点进退两难。经过几个月的 Macro/vba 项目工作后,事情变得庞大(大约 4K 行代码),并且由于它必须多次循环遍历许多列表,有时可能需要长达半小时才能完成,有时甚至会停止它拥有没有任何明显原因(或错误消息)。
我发现即使在关闭屏幕更新后,虽然速度更快,但仍然可以为我节省大约 5/10 分钟的处理时间。
所以我的问题是这样的:
如果程序不使用 "Variable = Cells(1, x)" 而是使用 "Variable = Worksheets("Sheet1").Cells(1,x),速度会有多大差异“?
因为它在整个过程中只在选项卡之间切换了两次,是否值得开始重写它以使其不会?
请考虑到我所有的 VBA 知识都是通过反复试验自学的,所以尽可能使用小词。
编辑:
我得到 3 张包含一堆数据的工作表(我不创建这些数据,也不能更改显示信息的内容/方式)。
表 A 列出了我公司的客户以及每个帐户的负责人。
表 B 包含详细说明这些客户在过去 2 年内购买/出售给我公司的商品的数据(包括成本、收入、规模、产品等信息)。
表 C 包含我们设法为哪些新产品做出新的“承诺”(因此,如果我们的一名工人设法让公司 1 声称他们将从现在开始向我们购买产品 X,那么该“承诺” ' 将在此处列出)。
我被要求做的(和我的项目)首先用表格 B 中的信息填写表格 A 上每个客户的详细信息(因此对于客户 A,我们现在将看到他们在2014),然后它将创建一个新表,显示表 C 中已在表 B 中“履行”的所有“承诺”(我们详细说明这些的原因是因为提供的价格(每单位)将低于客户我们已经“承诺”购买一定数量。问题是许多客户利用这一点,承诺的比他们实际购买的要多,所以我们需要这样做,看看谁信守诺言)。
此时,我创建了一个“Main.xlsm”并将信息转储到那里,从那里我必须为我们的每个员工制作 Excel,只详细说明他们的客户信息(您可以使用大约 56 名员工)弄清楚为什么我拼命地把它变成一个程序,而不是每次他们要求这个“报告”(至少每月两次)时手工完成)。
我在不使用可怕的“.select”选项时遇到的主要问题是,我被要求为报告的某些部分提供特定格式,这高度依赖于多个变量。这以及大量检查以确保数据没有错误以某种方式使我的程序中的代码行变得“大量”,因为它们有很多东西,例如“如果去年他们的交付量增加了 X%,但他们支付的费用比以前少 Y%,那么你必须去 blablablabla,如果少了 Z%,你必须......”或“如果客户去年购买了但不是今年,你必须从工作表 A 中复制整行(包括格式)并将其放在您将命名为“No Purchases”的新工作表上。
我已经开始使用类似的东西:
With Range(Cells(row, detUnit), Cells(BottomRow, detUnit + 2)).Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
对于格式,但我也有很多:
Columns(prtFilesFORWINAnoMes2).Select
Selection.Replace What:=" ", Replacement:="", LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False
Columns(prtAfrKgAnoMes1).Select
Selection.Replace What:=" ", Replacement:="", LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False
我必须在哪里“删除”潜在的错误,无论是谁输入了表格 A、B、C 的数据都可能犯的(而且有很多)。
为了节省一大堆时间,我还得以各种方式重新组织各种工作表,所以我也有了
Rows("1:1").Select
Selection.AutoFilter
If ActiveSheet.AutoFilterMode = False Then Selection.AutoFilter
ActiveWorkbook.Worksheets(ActiveSheet.Name).AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets(ActiveSheet.Name).AutoFilter.Sort.SortFields.Add Key:=Cells(1, columnaDestiny), SortOn:=xlSortOnValues, Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets(ActiveSheet.Name).AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
ActiveWorkbook.Worksheets(ActiveSheet.Name).AutoFilter.Sort.SortFields.Add Key:=Cells(1, columnaOrigin), SortOn:=xlSortOnValues, Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets(ActiveSheet.Name).AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
几乎在每个 Sub 的开头。
例如,这是“查看工作表 C 中的承诺是否在工作表 B 中实现”中的主要代码
Do While row <= rowLength
first = CoutryReference 'I've got to know which is the main country
Select Case Cells(row, detOrigin)
Case Is = CoutryReference
dir = "EXPORT" 'Determine if it's Export / Import / Xtrade
second = Cells(row, detDestiny)
Case Else
If Cells(row, detDestiny) = CoutryReference Then
dir = "IMPORT"
second = Cells(row, detOrigin)
Else
dir = "XTRADE"
first = Cells(row, detOrigin)
second = Cells(row, detDestiny)
End If
End Select
If SearchInForwin(dir, first, second, Cells(row, detClient), Cells(row, detProduct)) = True Then Call FoundLine(row, dir)
'SearchInForwin will loop through the (already organized) list in Sheet B and if it finds a match
' it will copy that line to Sheet "Fulfilled" and return "TRUE"
' FoundLine will then copy the line we're currently reading the information from and paste it into "Fulfilled" as well
row = row + 1
Loop
这就是 SearchInForwin:
Function SearchInForwin(direction As String, onecountry As String, othercountry As String, company As String, mode As String) As Boolean
Sheets("SHEET B").Select
Dim foUnd As Boolean, lookingRow As Long
lookingRow = lastHiddenWon 'Since it's alphabetical by Company, with
foUnd = False ' this we can jump to the last one found and start from there
Do While lookingRow <= Cells(Rows.Count, forwOrigen).End(xlUp).row
If Cells(lookingRow, forwEmpresa) = company Then
foUnd = True 'First Loop it to quickly determine if there's a simple match
If Cells(lookingRow, forwDireccion) = direction Then GoTo SecondBuc
End If
If (Cells(lookingRow, forwEmpresa) <> company And foUnd = True) Or Cells(lookingRow, forwAno) < yearAno Then
foUnd = False 'This is because we should only take into account purchase data from the latest year (and it's pre-organized so the most recent data is on the top of the list)
GoTo FIn
End If
lookingRow = lookingRow + 1
Loop
SecondBuc:
foUnd = False
Do While Cells(lookingRow, forwEmpresa) = company And Cells(lookingRow, forwDireccion) = direction And Cells(lookingRow, forwAno) = yearAno
'The conditions are the only thing that keeps this second loop extremely short
If Cells(lookingRow, forwAno) = yearAno And Cells(lookingRow, forwDestino) = othercountry And _
Cells(lookingRow, forwOrigen) = onecountry And InStr(1, Cells(lookingRow, forwTIPO), mode) > 0 Then
Call CopyToHidden(lookingRow, mode) 'Copies the line
foUnd = True
lastHiddenWon = lookingRow + 1
End If
lookingRow = lookingRow + 1
Loop
FIn:
SearchInForwin = foUnd
End Function
我可以上传我的模块的 .bas,但所有 cmets/变量都是西班牙语,因为我应该让可能需要查看它的潜在同事“理解”它(意思是他们想要如果他们愿意,可以解雇我并让其他人继续我的工作)
【问题讨论】:
-
您可能应该研究一个数据库应用程序而不是 4K 行的 VBA。它似乎不适合这项工作。
-
你能把列表读入一个数组吗?这比访问电子表格上的单元格要快一个数量级。
-
您可能还想检查是否将单元格加载到数组中,然后在数组上执行循环会提高性能。看这个:support.microsoft.com/en-us/kb/213798这里有更多提示:msdn.microsoft.com/en-us/library/office/…
-
@MarioGarcia 因为你有工作代码,你可能会在 Code Review Stack Exchange 上得到更多的爱...
-
您真的需要对单元格的引用,还是只需要单元格中的值?目前你得到一个 Range 对象,它比它内部的原始值更重资源。