[英]VBA: Working with filtered rows and SpecialCells(xlCellTypeVisible) vs copying data into new sheet
I have an Excel workbook with 250,000 rows and 10 columns and I want to split up the data into different workbooks. 我有一个包含250,000行和10列的Excel工作簿,我想将数据拆分为不同的工作簿。 My idea was to filter the list so that Excel/VBA doesn't have to go through all 250,000 rows every time my code says to look for something in the data.
我的想法是过滤列表,以便每次我的代码说要在数据中查找内容时,Excel / VBA不必遍历所有250,000行。
However, I've run into one specific problem with Sort
and also have a general question regarding hidden rows and SpecialCells(xlCellTypeVisible)
. 但是,我遇到了
Sort
一个特定问题,并且对隐藏行和SpecialCells(xlCellTypeVisible)
也有一个一般性的问题。 First off, here's the code: 首先,下面是代码:
Option Explicit
Sub Filtering()
Dim wsData As Worksheet
Dim cell As Variant
Dim lRowData As Long, lColData As Long
'filter
Set wsData = ThisWorkbook.Sheets(1)
lRowData = wsData.Cells(Rows.Count, 1).End(xlUp).Row
wsData.Range("A:A").AutoFilter Field:=1, Criteria1:="Name1"
For Each cell In wsData.Range(wsData.Cells(2, 1), wsData.Cells(100, 1)).SpecialCells(xlCellTypeVisible)
Debug.Print cell.Value
Next cell
'sort
lColData = wsData.Cells(1, Columns.Count).End(xlToLeft).Column
wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).SpecialCells(xlCellTypeVisible).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes ' returns error because of SpecialCells
End Sub
wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).SpecialCells(xlCellTypeVisible).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes
. wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).SpecialCells(xlCellTypeVisible).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes
。 It only happens when I use SpecialCells(xlCellTypeVisible)
, so wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes
works. SpecialCells(xlCellTypeVisible)
时才会发生,因此wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes
有效的。 My thinking in using SpecialCells(xlCellTypeVisible)
was that only then VBA would skip the filtered cells. 我在使用
SpecialCells(xlCellTypeVisible)
时的想法是,只有VBA才会跳过过滤的单元格。 I've tried it out, though, and to me it seems .Sort
skips them anyway, with or without SpecialCells(xlCellTypeVisible)
- can someone confirm this? 我尝试了一下,但对我来说。无论是否有
SpecialCells(xlCellTypeVisible)
, .Sort
跳过它们-有人可以确认吗?
SpecialCells(xlCellTypeVisible)
. SpecialCells(xlCellTypeVisible)
。 With .Sort
I (maybe) don't? .Sort
我(也许)不? And this question will always pop up for any operation I'll do on these filtered lists. This made me wonder: should I work with my original sheet where part of the data is hidden or should I temporarily create a new sheet, copy only the data I need (= excluding the rows I've hidden with the filter) and then work with that? 这让我感到疑惑:我应该使用隐藏了部分数据的原始工作表还是应该临时创建一个新工作表,只复制所需的数据(=排除使用过滤器隐藏的行),然后工作接着就,随即? Would this new sheet make it quicker or easier in any way?
这张新纸可以使它更快或更容易吗? What is better in your experience?
您的经验中有什么更好的?
Your first error occurs when you attempt to copy nonadjacent cell or range selections eg multiple nonadjacent rows within the same column (A1, A3, A5). 当您尝试复制不相邻的单元格或范围选择(例如,同一列(A1,A3,A5)中的多个不相邻的行)时,会发生第一个错误。 This is because Excel "slides" the ranges together and pastes them as a single rectangle.
这是因为Excel将范围“滑动”在一起并将其粘贴为单个矩形。 Your visible special cells are nonadjacent, and therefore can't be copied as a single range.
您可见的特殊单元格不相邻,因此不能复制为单个范围。
It seems that excel is looping through all of the cells in your range, not just the visible ones. 似乎excel正在遍历您范围内的所有单元,而不仅仅是可见的单元。 Your debug.print is returning more rows than just those that are visible.
您的debug.print返回的行比可见行还要多。
I would take a different approach to tackling your problem by using arrays, which VBA is able to loop through extremely quickly compared to worksheets. 我将采用另一种方法来解决问题,即使用数组,与工作表相比,VBA能够非常快速地遍历数组。
Using this approach, I was able to copy 9k rows with 10 columns based on the value of the first column from a sample size of 190k in 4.55 seconds: 使用这种方法,我能够在4.55秒钟内从190k的样本大小基于第一列的值复制9k行和10列:
EDIT: I did some messing around with the arrays which brought the time down to 0.45 seconds to copy 9k rows based on the first column from an initial 190k using the following: 编辑:我做了一些弄乱的数组,使用以下命令将时间缩短至0.45秒,以便根据第一列从最初的190k复制9k行:
Option Explicit
Sub update_column()
Dim lr1 As Long, lr2 As Long, i As Long, j As Long, count As Long, oc_count As Long
Dim arr As Variant, out_arr As Variant
Dim start_time As Double, seconds_elapsed As Double
Dim find_string As String
start_time = Timer
' change accordingly
find_string = "looking_for"
With Sheets("Sheet1")
' your target column in which you're trying to find your string
lr1 = .Cells(Rows.count, "A").End(xlUp).Row
lr2 = 1
' all of your data - change accordingly
arr = .Range("A1:J" & lr1)
' get number of features matching criteria to determine array size
oc_count = 0
For i = 1 To UBound(arr, 1)
If arr(i, 1) = find_string Then
oc_count = oc_count + 1
End If
Next
' redim array
ReDim out_arr(oc_count, 9)
' write all occurrences to new array
count = 0
For i = 1 To UBound(arr, 1)
If arr(i, 1) = find_string Then
For j = 1 To 10:
out_arr(count, j - 1) = arr(i, j)
Next j
count = count + 1
End If
Next
' write array to your target sheet, change sheet name and range accordingly
Sheets("Sheet2").Range("A1:J" & (oc_count + 1)) = out_arr
End With
seconds_elapsed = Round(Timer - start_time, 2)
Debug.Print (seconds_elapsed)
End Sub
It isn't super clean and could probably do with some refining, but if speed is important (which it often seems to be), this should do the job well for you. 它不是超级干净,可能可以进行一些优化,但是如果速度很重要(通常看起来很重要),那么这对您来说应该做得很好。
As per bm13563 comment you are copying nonadjacent cells. 根据bm13563注释,您正在复制不相邻的单元格。 Also using a Sort will be altering your base data which could have an impact if you ever need to determine how it was initially ordered in the future.
同样,使用排序将改变您的基础数据,如果您需要确定将来如何对它们进行初步订购,这可能会产生影响。
Working with filters can become quite complex so a simpler (and not particularly slow) method could be to do a string search with your filtering value in your chosen column and then loop through the instances returned performing actions on each result. 使用过滤器可能会变得非常复杂,因此一种更简单(但并非特别慢)的方法可能是使用所选列中的过滤值进行字符串搜索,然后循环返回对每个结果执行操作的实例。
The (slightly adapted) code below from David Zemens would be a good starting point (copied from Find All Instances in Excel Column ) 大卫·泽门斯(David Zemens)的以下(略有改编)代码将是一个很好的起点(从“ Excel列中的查找所有实例”复制)
Sub foo()
Dim fnd As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim myRange As Range, LastCell As Range
Set huntRange = Range("A:B")
Set LastCell = myRange.Cells(myRange.Cells.Count)
Set FoundCell = myRange.Find(what:="January", after:=LastCell, LookIn:=xlValues)
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
Do
'Do your actions here, you can get the address of the found cell to return row etc.
MsgBox (FoundCell.Value)
Set FoundCell = myRange.FindNext(FoundCell)
Loop While (FoundCell.Address <> FirstFound)
End If
Set rng = FoundCell '<~~ Careful, as this is only the LAST instance of FoundCell.
End Sub
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.