现有一份销售明细,见下图,大约有会有2000行左右:
大约2000行销售明细中,有1200行0值
因为这里面有很多销售量为0的明细,为了后续操作方便,需要将C列销售量为0的记录删掉。
方法一:循环这是最容易想到的方法,实现起来也很容易,代码及注释如下:
Sub 删掉0行()
Rem 方法一: Do循环
Dim r As Integer
Dim t As Double
t = Timer
r = 2
Do While Range("A" & r).Value <> ""
If Range("C" & r).Value = 0 Then
Rem 如果C列为0,就删掉当前行
Rows(r).Delete
Rem 最重要的就是下面的r=r-1,如果不加这一行,【相邻两个0行的第二行】将不会被删掉。
Rem 原因:把第一个0行删掉后,第二个0行自动上移一行,而经过r=r 1后,r已经指向下一行,
rem 正好跳过了上移的这一行
r = r - 1
End If
r = r 1
Loop
MsgBox "删除完毕,用时 " & Format(Timer - t, "0.00") & " 秒", , ""
End Sub
该方法需要较长的时间,运行结果如下:
即使关闭屏幕更新,也需要0.16秒:(这说明关闭屏幕更新确实非常有效地提高了代码效率)
方法二:循环,标记,一次性删除方法一循环之所以效率慢,是因为不断删除行、不断将下面的内容上移一行。如果循环的时候不删除行,只是做个标记,然后一次性删除,那么效率应该会高很多。代码如下:
Sub 删掉0行_2()
Rem 方法二: 排序
Dim r As Integer
Dim t As Double
t = Timer
r = 2
Do While Range("A" & r).Value <> ""
If Range("C" & r).Value = 0 Then
Rem 只是在E列做个标记,并不删除改行
Range("E" & r).Value = 1
End If
r = r 1
Loop
Rem 定位E列列有内容的单元格,将这些单元格的整行一次性删掉
Range("E:E").SpecialCells(xlCellTypeConstants).EntireRow.Delete
MsgBox "删除完毕,用时 " & Format(Timer - t, "0.00") & " 秒", , ""
End Sub
在没有关闭屏幕更新的情况下(其实这种方法关闭、不关闭屏幕更新没有影响),用时仅仅0.07秒:
方法三:排序方法一循环之所以效率慢,是因为不断删除行、不断将下面的内容上移一行。是否能想办法只上移一次?这时候效率肯定高。方法很简单:排序。代码如下:
排序的代码太难写了,这时候VBA的巨大优势就体现出来了:录制宏。
Sub 删掉0行_3()
Dim r As Integer
Dim t As Double
t = Timer
Rem 以下一大坨代码实现按C列排序,完全录制的,稍加修改即可。不必深究。
ActiveSheet.Sort.SortFields.Clear
ActiveSheet.Sort.SortFields.Add Key:=Range("C2"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveSheet.Sort
.SetRange Range("A1").CurrentRegion
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Rem 在C列找第一个1出现的位置,从该位置往上直到第2行,都是应该被删除的。
Rem 当然这里面有个前提:事先已经知道C列销售量没有负数,且一定有销量为1的行
r = Range("C:C").Find(WHAT:=1).Row
Rem 下面一次性删除0行
Rows("2:" & r).Delete
MsgBox "删除完毕,用时 " & Format(Timer - t, "0.00") & " 秒", , ""
End Sub
只需要0.03秒:
这个方法最核心的操作是排序。排序操作在实际工作中经常用到,但是录制的代码实在太恶心,而且也没必要去记住这么复杂的代码,因此可以将常用的单条件排序、二条件排序、三条件排序做成Sub过程,需要时直接调用即可。该Sub过程将在后续文章中展示。
这种方法非常快,但是有个问题:对原始数据进行了排序,因此打乱了原始数据的顺序。解决方法:排序前在E列顺序填写1、2、3……,删除完毕后,再按E列升序排序,然后删掉E列,即可恢复到原来的顺序。很简单,代码不再展示。这种方法是既想排序、又不想打乱原始数据顺序的最好方法,后续可能会陆续用到。
方法四:公式法这个问题我所能想到的终极方法是用公式:在E列计算C列是否为0,然后替换掉False值,然后将True值的整行删掉。代码量很小,且代码不像方法三那么“恶心难记”,速度也很快。代码如下:
Sub 删掉0行_4()
Dim t As Double
t = Timer
Rem 在E列填充公式
Range("E2").Value = "=C2=0"
Range("E2").AutoFill Destination:=Range("E2:E" & Range("A1").CurrentRegion.Rows.Count)
With Range("E:E")
Rem 选择性粘贴数值,去掉公式
.Copy
Range("E1").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Rem 取消复制剪切状态,可以提升下面的Replace操作的效率
Application.CutCopyMode = False
Rem 将False值替换为空白
.Replace WHAT:="FALSE", REPLACEMENT:=""
Rem 所有的非空白值(TRUE)就是要删掉的行
.SpecialCells(xlCellTypeConstants).EntireRow.Delete
End With
MsgBox "删除完毕,用时 " & Format(Timer - t, "0.00") & " 秒", , ""
End Sub
以上填充公式、去掉公式的操作很繁琐,所以显得代码量比较大,实际上可以用一个自定义的Sub过程【填充并去掉公式】搞定,该Sub过程将在后面展示。代码如下:
Sub 删掉0行_5()
Dim t As Double
t = Timer
Rem 在E列填充公式
Call 填充并去掉公式(Range("E2"), "=C2=0", True)
With Range("E:E")
.Replace WHAT:="FALSE", replacement:=""
.SpecialCells(xlCellTypeConstants).EntireRow.Delete
End With
MsgBox "删除完毕,用时 " & Format(Timer - t, "0.00") & " 秒", , ""
End Sub
以上几种方法,希望能对大家有所帮助。
I am a VBAer!
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved