简体   繁体   English

VBA:处理过滤的行和SpecialCells(xlCellTypeVisible)与将数据复制到新表中

[英]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
  1. "Run-time error '1004': This can't be done on a multiple range selection. Select a single range and try again." “运行时错误'1004':无法选择多个范围。选择单个范围,然后重试。” This occurs in the last line, in 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跳过它们-有人可以确认吗?

  1. And this leads to my more general question: One thing I'm not quite clear on is when does Excel/VBA skip filtered rows and when it doesn't. 这就引出了我的一个更笼统的问题:我不清楚的一件事是Excel / VBA何时跳过过滤的行,何时不跳过。 To loop through the visible cells, I need to use 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? 您的经验中有什么更好的?

  1. 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. 您可见的特殊单元格不相邻,因此不能复制为单个范围。

  2. 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.

相关问题 sheet.SpecialCells(xlCellTypeVisible)中的范围也选择了空白行,尽管应用了VBA过滤器 - range in sheet.SpecialCells(xlCellTypeVisible) also selecting blank rows although a filter was applied VBA SpecialCells(xlCellTypeVisible)在UDF中不起作用 - SpecialCells(xlCellTypeVisible) not working in UDF 使用.Hidden或.SpecialCells(xlCellTypeVisible)来忽略隐藏的行 - 不工作 - Using .Hidden or .SpecialCells(xlCellTypeVisible) to Ignore Hidden Rows — Not Working SpecialCells(xlCellTypeVisible)还包括隐藏/过滤的单元格 - SpecialCells(xlCellTypeVisible) also includes hidden/filtered cells 在 excel vba 中选择了 SpecialCells(xlCellTypeVisible) 额外行 - SpecialCells(xlCellTypeVisible) extra row is selected in excel vba SpecialCells(xlCellTypeVisible) - SpecialCells(xlCellTypeVisible) 遍历过滤器中的枢轴项时,vba SpecialCells(xlCellTypeVisible)无法正常工作 - vba SpecialCells(xlCellTypeVisible) not working correctly when looping through pivot items in a filter 将过滤后的范围放入数组 - ....AutoFilter.Range.SpecialCells(xlCellTypeVisible).Offset(1) - Get a filtered range into an array - ….AutoFilter.Range.SpecialCells(xlCellTypeVisible).Offset(1) VBA通过.SpecialCells(xlCellTypeVisible)合并自动过滤的单元格 - VBA merging autofiltered cells via .SpecialCells(xlCellTypeVisible).Range SpecialCells(xlCellTypeVisible)-自动过滤器返回零行时出错 - SpecialCells(xlCellTypeVisible) - Error when Autofilter returns zero rows
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM