4
\$\begingroup\$

May be my question is not up to the standard for Code Review, but of upmost importance for reassessing methods used in my VBA coding . While preparing/testing answer for a SO post (thought to simplest of simplest questions) VBA cell format that contain a specific percentage value, I came to a jolt that shaken whatever little confidence I had in VBA.

I found that OP had looped more than once in the cells of Range in question and used to select each cell and test its value and then format border of the cell with desired parameters. As a thumb rule to minimize interaction with excel cell and to avoid select etc, I simply planned for go for each cell iteration of the range and test the values and according to make a union range object and finally format that range in a single go.

Actually I am ashamed about the code and don’t want to reproduce it here and only concerned about the methods suitable for this type of operation. However since code review rule calls for at least 3 lines of code I am forced to paste (the never going to be finished) code here and request to ignore completely.

Sub test()
Dim lr As Long
Dim c As Range, Rng As Range, Grt100Rng As Range, Less0Rng As Range
lr = Range("G" & Rows.Count).End(xlUp).Row
Set Rng = Range("G3:G" & lr)  
Dim tm As Double
tm = Timer
    For Each c In Rng
        If c >= 1 Then
            If Grt100Rng Is Nothing Then
            Set Grt100Rng = c
            Else
            Set Grt100Rng = Union(Grt100Rng, c)
            End If
        End If
        If c <= 0 Then
            If Less0Rng Is Nothing Then
            Set Less0Rng = c
            Else
            Set Less0Rng = Union(Less0Rng, c)
            End If
        End If
    Next

  Debug.Print "Union at :" & Timer - tm
  If Not Grt100Rng Is Nothing Then
  With Grt100Rng.Borders
  If GreaterThan100.Value Then
  .Color = vbBlue
  .LineStyle = xlContinuous
  .Weight = xlThick
  Else
  .Color = vbBlack
  .LineStyle = xlNone
  .Weight = xlThin
  End If
  End With


  With Less0Rng.Borders
  If LessThan0.Value Then
  .Color = vbBlue
  .LineStyle = xlContinuous
  .Weight = xlThick
  Else
  .Color = vbBlack
  .LineStyle = xlNone
  .Weight = xlThin
  End If
  End With
  End If
  Debug.Print Timer - tm
End Sub

Similar working approach already tried successfully in my SO answer referred below. But this time, the above code cruelly backfired on me and taken around 80 sec for processing 10 k rows.

Testing the values of the range from a 2D Array taken in single command ( Arr=Rng.value) from the range object and tried to create union range from the array row address took nearly same amount of time. When tested the OP code found it takes only 12-14 sec to process with screen updating on and 1.2 sec with screen updating off.

I refrain from answering the post leaving comments to help OP to create Conditional format. I somehow realized (or rather conceptualize ) reading and formatting cells would be faster than creating union range and union range would only prove better option when writing cells,

I started testing on new Range of 10-50 K rows various option to find optimized condition when creating Union range would be efficient over brute force looping and formatting cell. At testing of 10 K rows I start finding union method is being far less efficient than brute force looping. At 50 K it never finished the process.

As the testes are being time consuming I thought of asking for experts opinion in Code Review and get what would be the optimized scenario for creating using union range for conditional formatting (in VBA) over brute force looping and formatting.

In this context, I must refer to my answer in SO post Is it possible to speed-up background / text / border formatting?. In this case, Union Range method reduced process time to a odd second from around 30 minutes.

If auto filtering and creating range of SpecialCells(xlCellTypeVisible) is a solution, that also proved to raise error 1004 "Ms excel cannot create or use data range reference because too complex” at around range of 150 K rows. Tried and failed at SO Post. What is the limitation of Union range?

It made my confidence shaken. May some experts please clarify, If union range method is always less efficient, why this above referred answer brought down the time to a odd second from around 30 minutes. Any explanation,advice, good reading or information on the matter would be a bonanza.

Edit: I want to share the result of simple tests carried out today to test limit of creating range of SpecialCells(xlCellTypeVisible). To keep the original post length readable, I am deleting this section and posting it as answer.

\$\endgroup\$
3
  • \$\begingroup\$ There are a variety of resources to review limitations of the UNION function. It looks like the post on SO was possibly based on this site, and it's instructive to review the limitations lower down on that page. Also, Craig Pearson has an excellent updated ProperUnion function that addresses many of the limitations. \$\endgroup\$
    – PeterT
    Jul 25, 2019 at 20:54
  • \$\begingroup\$ This Excel reference page implies that the Union limit may be 2,147,483,648 cells (see "Noncontiguous cells that can be selected"). But I'm not convinced that is the correct number. In any case, I completely agree with the other answers on SO that the correct approach is conditional formatting. \$\endgroup\$
    – PeterT
    Jul 25, 2019 at 20:56
  • \$\begingroup\$ @PeterT, Many Thanks for going through the tiring question and prompt response. I wll go through all the links and come back. \$\endgroup\$
    – Ahmed AU
    Jul 25, 2019 at 23:39

3 Answers 3

4
\$\begingroup\$

75K Non-Contiguous Areas, No Problem

My FastUnion class was able to crack the 75K non-contiguous areas goal by expanding on Ahmed AU answer using Union() with multiple parameters. Although, this class excels at smaller numbers of areas, my UnionCollection class far out performs it by working with smaller groups of cells at a time.

enter image description here

Results

Immediate Window ScreenShot

FastUnion:Class

Option Explicit
Private Const MaxArgs As Long = 30
Private Groups(1 To MaxArgs) As Range
Private Index As Long
Private Count As Long
Private Compacted As Boolean

Public Sub Add(ByRef NewRange As Range)
    If Count < MaxArgs Then Count = Count + 1
    Index = Index + 1
    If Index > MaxArgs Then Index = IIf(Compacted, 2, 1)
    If Groups(Index) Is Nothing Then
        Set Groups(Index) = NewRange
    Else
        Set Groups(Index) = Union(Groups(Index), NewRange)
    End If
End Sub

Private Sub Compact()
    Select Case Count
        Case 2
            Set Groups(1) = Union(Groups(1), Groups(2))
        Case 3
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3))
        Case 4
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4))
        Case 5
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5))
        Case 6
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6))
        Case 7
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7))
        Case 8
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8))
        Case 9
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9))
        Case 10
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10))
        Case 11
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11))
        Case 12
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12))
        Case 13
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13))
        Case 14
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14))
        Case 15
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15))
        Case 16
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16))
        Case 17
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17))
        Case 18
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18))
        Case 19
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19))
        Case 20
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20))
        Case 21
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21))
        Case 22
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22))
        Case 23
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23))
        Case 24
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24))
        Case 25
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24), Groups(25))
        Case 26
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24), Groups(25), Groups(26))
        Case 27
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24), Groups(25), Groups(26), Groups(27))
        Case 28
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24), Groups(25), Groups(26), Groups(27), Groups(28))
        Case 29
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24), Groups(25), Groups(26), Groups(27), Groups(28), Groups(29))
        Case 30
            Set Groups(1) = Union(Groups(1), Groups(2), Groups(3), Groups(4), Groups(5), Groups(6), Groups(7), Groups(8), Groups(9), Groups(10), Groups(11), Groups(12), Groups(13), Groups(14), Groups(15), Groups(16), Groups(17), Groups(18), Groups(19), Groups(20), Groups(21), Groups(22), Groups(23), Groups(24), Groups(25), Groups(26), Groups(27), Groups(28), Groups(29), Groups(30))
    End Select
    
    Dim n As Long
    For n = 2 To MaxArgs
        Set Groups(n) = Nothing
    Next
    Index = 2
    Compacted = True
    Count = 0
    
End Sub

Public Function getRange() As Range
    Compact
    Set getRange = Groups(1)
End Function

UnionCollection:Class

The Default number of cells in a group is set to 500 which may not be optimal. The optimal group size could be determined by testing different values for the CellCountGoal.

Option Explicit
Private Const DefaultCellCountGoal As Long = 500
Private RangeItems As New Collection
Private item As Range
Public CellCountGoal As Long

Public Sub Add(ByRef NewRange As Range)
    If item Is Nothing Then
        Set item = NewRange
    Else
        Set item = Union(item, NewRange)
    End If
    
    If item.CountLarge >= CellCountGoal Then Compact

End Sub

Private Sub Class_Initialize()
    CellCountGoal = DefaultCellCountGoal
End Sub

Public Function Items() As Collection
    Compact
    Set Items = RangeItems
End Function

Private Sub Compact()
    If Not item Is Nothing Then
        RangeItems.Add item
        Set item = Nothing
    End If
End Sub

Module1

Option Explicit

Sub TestFastUnion()
    Application.ScreenUpdating = False
    Debug.Print "TestFastUnionRange Results:"
    Debug.Print "Area Count", "UnionTime", "FormatTime", "ProcTime"

    TestFastUnionRange 1000, 2000, 3000, 5000, 10000, 75000
    
    Debug.Print
    Debug.Print "TestUnionCollection Results:"
    Debug.Print "Area Count", "UnionTime", "FormatTime", "ProcTime"
    
    TestUnionCollection 1000, 2000, 3000, 5000, 10000, 75000
    
    
End Sub

Sub TestFastUnionRange(ParamArray AreaCounts() As Variant)
    Dim AllCells As Range, Cell As Range
    Dim ProcTime As Double, FormatTime As Double, UnionTime As Double
    Dim NewUnion As FastUnion
    Dim AreaCount
    
    For Each AreaCount In AreaCounts
        Cells.ClearFormats
        Debug.Print AreaCount,
        ProcTime = Timer
        Set NewUnion = New FastUnion
        
        For Each Cell In Range("A1").Resize(AreaCount * 2)
            If Cell.Row Mod 2 = 0 Then NewUnion.Add Cell
        Next
        Set AllCells = NewUnion.getRange
        UnionTime = Round(Timer - ProcTime, 2)
        ApplyBorderFormmating AllCells, vbRed
        ProcTime = Round(Timer - ProcTime, 2)
        FormatTime = Round(ProcTime - UnionTime, 2)
        Debug.Print UnionTime, FormatTime, ProcTime
    Next
End Sub

Sub TestUnionCollection(ParamArray AreaCounts() As Variant)
    Dim Cell As Range, item As Range
    Dim ProcTime As Double, FormatTime As Double, UnionTime As Double
    Dim NewUnion As UnionCollection
    Dim AreaCount
    
    For Each AreaCount In AreaCounts
        Cells.ClearFormats
        Debug.Print AreaCount,
        ProcTime = Timer
        Set NewUnion = New UnionCollection
        
        For Each Cell In Range("A1").Resize(AreaCount * 2)
            If Cell.Row Mod 2 = 0 Then NewUnion.Add Cell
        Next
        
        UnionTime = Round(Timer - ProcTime, 2)
        For Each item In NewUnion.Items
            ApplyBorderFormmating item, vbRed
        Next
        
        ProcTime = Round(Timer - ProcTime, 2)
        FormatTime = Round(ProcTime - UnionTime, 2)
        Debug.Print UnionTime, FormatTime, ProcTime
    Next
End Sub

Sub ApplyBorderFormmating(Target As Range, Color As Single)
    With Target.Borders
        .Color = Color
        .LineStyle = xlContinuous
        .Weight = xlThick
    End With
End Sub

Sub PrintCases()
    Dim list As Object
    Set list = CreateObject("System.Collections.ArrayList")
    Dim n As Long
    For n = 1 To 30
        list.Add "Groups(" & n & ")"
        Debug.Print String(2, vbTab); "Case "; n
        Debug.Print String(3, vbTab); "Set AllCells = Union("; Join(list.ToArray, ","); ")"
    Next
End Sub

Edit

I modified the FastUnion class after I realized it would reset the range after Compact() was ran.

The OP pointed out I should list my specs.

System Specs

  • 64 bit Office 365
  • 6 GB Ram
  • 2.3 MHz processor

Addendum

Here was my first attempt at cracking 75 K areas. It performed very well with smaller number of unions but started to slow down exponentially after 20 K unions. Although, it probably isn't practical, there may be some merit to combining it with the FastUnion. If nothing else it was interesting to write.

StingUnion:Class

Option Explicit
Private Const MaxAddressSize As Long = 255
Private CurrentLength As Long
Private Result As Range
Private Parent As Worksheet
Private AddressHolder As String

Public Sub Add(Source As Range)
    If Parent Is Nothing Then
        Set Parent = Source.Parent
        AddressHolder = Space(MaxAddressSize)
    End If
    
    Dim length As Long
    Dim Address As String
    Address = Source.Address(0, 0)
    length = Len(Address)
    
    If (length + CurrentLength) > MaxAddressSize Then Compact
    
    If CurrentLength = 0 Then
        Mid(AddressHolder, CurrentLength + 1, length + 1) = Address
    Else
        Mid(AddressHolder, CurrentLength + 1, length + 1) = "," & Address
    End If
    CurrentLength = CurrentLength + length + 1
    
End Sub

Public Sub Compact()
    If CurrentLength = 0 Then Exit Sub
    
    If Result Is Nothing Then
        Set Result = Parent.Range(AddressHolder)
    Else
        Set Result = Union(Result, Parent.Range(AddressHolder))
    End If
    
    CurrentLength = 0
    AddressHolder = Space(MaxAddressSize)
End Sub

Function getRange() As Range
    Compact
    Set getRange = Result
End Function
\$\endgroup\$
6
  • \$\begingroup\$ First of all, Thanks for your valuable time spend on the post. I will go through it in details and come back. However at a glance, congratulate for brilliant maximum utilization of 32 argument property of Union rage into a class. +1 for that before going in details and please mention excel version used for readers . I will try it in 2007. \$\endgroup\$
    – Ahmed AU
    Aug 17, 2019 at 6:37
  • \$\begingroup\$ @AhmedAU Are you running 32 or 64 bit Office? \$\endgroup\$
    – TinMan
    Aug 17, 2019 at 10:35
  • \$\begingroup\$ may treat me as Old School, still with 32 bit. Still not find time to test or study in details, may be delayed longer. Please bear .with me. \$\endgroup\$
    – Ahmed AU
    Aug 17, 2019 at 12:01
  • \$\begingroup\$ the codes are like poetry and running them is like music from violin. I also created another class combining FastUnoin with UnionCollenction. After number of testing with varying conditions observed that in general Brute force Looping, Combined Fast loop_Collection method and Union_Collection method taking more or less same time (19s for 30K) for high Area counts.But final conclusion and salute to you, with ** UnionCollenction reducing CellCountGoal to 100 brought down ProcTime to 4.3 from 19 is the best performer in this context. ** \$\endgroup\$
    – Ahmed AU
    Aug 18, 2019 at 5:55
  • \$\begingroup\$ Another point the Combined Fast loop_Collection method gives best time of 0.18 (18.2 is formatting time only. for 30K with CellCountGoal of 100) for Creating range collection. This deemed fit for other application where creation of union range is absolute necessary. Finally again I have no word to thank you for wasting your valuable time on such boring topic. \$\endgroup\$
    – Ahmed AU
    Aug 18, 2019 at 5:58
1
\$\begingroup\$

I am not saying how this is going to help, but I did the Same test on My system (Office 2010) couldn't find 2007.

So for the Test of the Range SpecialCells(xlCellTypeVisible), it took less than a second in my system to complete the Range as in your Answer using the same code on a Blank sheet.

enter image description here

Result in Debug.

Success at 1000 Range Area Count 250
Success at 2000 Range Area Count 500
Success at 3000 Range Area Count 750
Success at 4000 Range Area Count 1000
Success at 5000 Range Area Count 1250
Success at 6000 Range Area Count 1500
Success at 7000 Range Area Count 1750
Success at 8000 Range Area Count 2000
Success at 9000 Range Area Count 2250
Success at 10000 Range Area Count 2500
Success at 11000 Range Area Count 2750
Success at 12000 Range Area Count 3000
Success at 13000 Range Area Count 3250
Success at 14000 Range Area Count 3500
Success at 15000 Range Area Count 3750
Success at 16000 Range Area Count 4000
Success at 17000 Range Area Count 4250
Success at 18000 Range Area Count 4500
Success at 19000 Range Area Count 4750
Success at 20000 Range Area Count 5000
Success at 21000 Range Area Count 5250
Success at 22000 Range Area Count 5500
Success at 23000 Range Area Count 5750
Success at 24000 Range Area Count 6000
Success at 25000 Range Area Count 6250
Success at 26000 Range Area Count 6500
Success at 27000 Range Area Count 6750
Success at 28000 Range Area Count 7000
Success at 29000 Range Area Count 7250
Success at 30000 Range Area Count 7500
Success at 31000 Range Area Count 7750
Success at 32000 Range Area Count 8000
Success at 33000 Range Area Count 8250
Success at 34000 Range Area Count 8500
Success at 35000 Range Area Count 8750
Success at 36000 Range Area Count 9000
Success at 37000 Range Area Count 9250
Success at 38000 Range Area Count 9500
Success at 39000 Range Area Count 9750
Success at 40000 Range Area Count 10000
Success at 41000 Range Area Count 10250
Success at 42000 Range Area Count 10500
Success at 43000 Range Area Count 10750
Success at 44000 Range Area Count 11000
Success at 45000 Range Area Count 11250
Success at 46000 Range Area Count 11500
Success at 47000 Range Area Count 11750
Success at 48000 Range Area Count 12000
Success at 49000 Range Area Count 12250
Success at 50000 Range Area Count 12500
Success at 51000 Range Area Count 12750
Success at 52000 Range Area Count 13000
Success at 53000 Range Area Count 13250
Success at 54000 Range Area Count 13500
Success at 55000 Range Area Count 13750
Success at 56000 Range Area Count 14000
Success at 57000 Range Area Count 14250
Success at 58000 Range Area Count 14500
Success at 59000 Range Area Count 14750
Success at 60000 Range Area Count 15000
Success at 61000 Range Area Count 15250
Success at 62000 Range Area Count 15500
Success at 63000 Range Area Count 15750
Success at 64000 Range Area Count 16000
Success at 65000 Range Area Count 16250
Success at 66000 Range Area Count 16500
Success at 67000 Range Area Count 16750
Success at 68000 Range Area Count 17000
Success at 69000 Range Area Count 17250
Success at 70000 Range Area Count 17500
Success at 71000 Range Area Count 17750
Success at 72000 Range Area Count 18000
Success at 73000 Range Area Count 18250
Success at 74000 Range Area Count 18500
Success at 75000 Range Area Count 18750
Success at 76000 Range Area Count 19000
Success at 77000 Range Area Count 19250
Success at 78000 Range Area Count 19500
Success at 79000 Range Area Count 19750
Success at 80000 Range Area Count 20000
Success at 81000 Range Area Count 20250
Success at 82000 Range Area Count 20500
Success at 83000 Range Area Count 20750
Success at 84000 Range Area Count 21000
Success at 85000 Range Area Count 21250
Success at 86000 Range Area Count 21500
Success at 87000 Range Area Count 21750
Success at 88000 Range Area Count 22000
Success at 89000 Range Area Count 22250
Success at 90000 Range Area Count 22500
Success at 91000 Range Area Count 22750
Success at 92000 Range Area Count 23000
Success at 93000 Range Area Count 23250
Success at 94000 Range Area Count 23500
Success at 95000 Range Area Count 23750
Success at 96000 Range Area Count 24000
Success at 97000 Range Area Count 24250
Success at 98000 Range Area Count 24500
Success at 99000 Range Area Count 24750
Success at 100000 Range Area Count 25000
Success at 101000 Range Area Count 25250
Success at 102000 Range Area Count 25500
Success at 103000 Range Area Count 25750
Success at 104000 Range Area Count 26000
Success at 105000 Range Area Count 26250
Success at 106000 Range Area Count 26500
Success at 107000 Range Area Count 26750
Success at 108000 Range Area Count 27000
Success at 109000 Range Area Count 27250
Success at 110000 Range Area Count 27500
Success at 111000 Range Area Count 27750
Success at 112000 Range Area Count 28000
Success at 113000 Range Area Count 28250
Success at 114000 Range Area Count 28500
Success at 115000 Range Area Count 28750
Success at 116000 Range Area Count 29000
Success at 117000 Range Area Count 29250
Success at 118000 Range Area Count 29500
Success at 119000 Range Area Count 29750
Success at 120000 Range Area Count 30000
Success at 121000 Range Area Count 30250
Success at 122000 Range Area Count 30500
Success at 123000 Range Area Count 30750
Success at 124000 Range Area Count 31000
Success at 125000 Range Area Count 31250
Success at 126000 Range Area Count 31500
Success at 127000 Range Area Count 31750
Success at 128000 Range Area Count 32000
Success at 129000 Range Area Count 32250
Success at 130000 Range Area Count 32500
Success at 131000 Range Area Count 32750
Success at 132000 Range Area Count 33000
Success at 133000 Range Area Count 33250
Success at 134000 Range Area Count 33500
Success at 135000 Range Area Count 33750
Success at 136000 Range Area Count 34000
Success at 137000 Range Area Count 34250
Success at 138000 Range Area Count 34500
Success at 139000 Range Area Count 34750
Success at 140000 Range Area Count 35000
Success at 141000 Range Area Count 35250
Success at 142000 Range Area Count 35500
Success at 143000 Range Area Count 35750
Success at 144000 Range Area Count 36000
Success at 145000 Range Area Count 36250
Success at 146000 Range Area Count 36500
Success at 147000 Range Area Count 36750
Success at 148000 Range Area Count 37000
Success at 149000 Range Area Count 37250
Success at 150000 Range Area Count 37500

For Next Loop, it was taking way long, as you said increasing with every loop, inFor Each Cel in Range.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for taking interest. The SpecialCells limit is fixed in 2010. Could you please feedback about 32K limit of Union Range. \$\endgroup\$
    – Ahmed AU
    Aug 5, 2019 at 0:27
0
\$\begingroup\$

I want to share the result of simple tests carried out today to test limit of creating range of SpecialCells(xlCellTypeVisible). Only to keep the original post length readable, I am posting it as answer.

I filled the range A2:A150001 with 1 n number of times (each area length) with one 0 cell and filtered for 1 with code.

Sub FillRange()
Dim Arr(1 To 150000, 1 To 1), Rw As Long, xInt As Integer, AreaLen As Integer, Cnt As Integer
AreaLen = InputBox("Enter Each area Length", , 3)
Cnt = 0
    For Rw = 1 To 150000
        If Cnt > AreaLen - 1 Then
        Cnt = 0
        xInt = 0
        Else
        Cnt = Cnt + 1
        xInt = 1
        End If
    Arr(Rw, 1) = xInt
    Next
Range("A2:A150001").Value = Arr
ActiveSheet.Range("$A$1:$A$150001").AutoFilter Field:=1, Criteria1:="1"
End Sub

And then used the following code in step of 1k to creating range of SpecialCells(xlCellTypeVisible) and find where error 1004 creeps in

Sub TestSpecialCellRange()
Dim Rw As Long, Rng As Range
    For Rw = 1000 To 150000 Step 1000
    Set Rng = Nothing
    On Error Resume Next
    Set Rng = Range("A2:A" & Rw).SpecialCells(xlCellTypeVisible)
      If Err <> 0 Then
      Debug.Print "Error at " & Rw & " Error No " & Err.Number & " : " & Err.Description
      Err.Clear
      On Error GoTo 0
      Exit For
      Else
      Debug.Print "Success at " & Rw & " Range Area Count " & Rng.Areas.Count
      End If
    Next
End Sub

Following are the result of debug window

At area length 1
Success at 15000 Range Area Count 7500
Success at 16000 Range Area Count 8000
Error at 17000 Error No 1004 : Microsoft Office Excel cannot create or use the data range reference because it is too complex. Try one or more of the following:
• Use data that can be selected in one contiguous rectangle.
• Use data from the same sheet.

At area length 2
Success at 23000 Range Area Count 7667
Success at 24000 Range Area Count 8000
Error at 25000 Error No 1004 : Microsoft Office Excel cannot create or use the data range reference 

At area length 3
Success at 32000 Range Area Count 8000
Error at 33000 Error No 1004 : Microsoft Office Excel cannot create or use the data range reference because it is too complex. Try one or more of the following:

At area length 4
Success at 48000 Range Area Count 8000
Success at 49000 Range Area Count 8167
Error at 50000 Error No 1004 : Microsoft Office Excel cannot create or use the data range reference 


At area length 10
Success at 88000 Range Area Count 8000
Success at 89000 Range Area Count 8091
Success at 90000 Range Area Count 8182
Error at 91000 Error No 1004 : Microsoft Office Excel cannot create or use the data range reference 

Finally at area length 19 I succeed to cover 150 K
Success at 149000 Range Area Count 7096
Success at 150000 Range Area Count 7143

So it may be concluded that irrespective of number of rows covered, at around 8k non contagious area of the range, the error 1004 creeps in. I also tried with covering columns of the range 1 to 5 it is always same. however this is in my old good laptop with excel 2007 only, don't know what high performance machines result would be.

Edit: Next while testing iterating For each Cell in Range and adding to Union range (If condition meets) with simple code like

Set Rng = Range("A1")   ' To avoid testing "if Rng is nothing" at each row
    For Each Cel In Range("A2:A150000")
    Rw = Cel.Row
        If Cel.Value = 1 Then
        Set Rng = Union(Rng, Cel)
        End If
        If Rw Mod 1000 = 0 Then
        AreaCnt = Rng.Areas.Count
        Debug.Print " Row: " & Rw & " Range Area  Count : " & AreaCnt & " at " & Timer - tm
        DoEvents
        End If
    Next

The results shown normal behavior of union method. The time taken to process 1000 number of rows increases as the range grows heavy with non contiguous areas. Time to process 1 K cell increases to around 60 sec (at start it is 0.125 sec only) when range area count grows around 1000. Unable to achieve my target of 150 K rows with 75 K non contiguous area.

I tweak my code to utilize 30 parameter limit of Union syntax with range array. Encouraged by improvement in performance, I further tweak the code to utilize second level of union with range array with further plan to increase level to optimize performance.

Sub CellUnion3()
Dim Rw As Long, Rng(1 To 30) As Range, AreaCnt As Long, Arr(1 To 150001) As Variant
Dim Cel As Range, Rslt(1 To 30) As Range, FinalRslt As Range
Dim tm As Double, Cnt As Long, Cnt2 As Long

Set FinalRslt = Range("A1")   ' to avoid testing if Rng is nothing at each row
Cnt = 0
Cnt2 = 0
tm = Timer
SecCnt = 0

    For Each Cel In Range("A2:A150000")

        If Cel.Value = 1 Then
        Cnt = Cnt + 1
        Rw = Cel.Row

            If Cnt <= 30 Then
            Set Rng(Cnt) = Cel
            End If

            If Cnt = 30 Then
            Cnt = 0
            Cnt2 = Cnt2 + 1
            Set Rslt(Cnt2) = Union(Rng(1), Rng(2), Rng(3), Rng(4), Rng(5), Rng(6), Rng(7), Rng(8), Rng(9), Rng(10), _
            Rng(11), Rng(12), Rng(13), Rng(14), Rng(15), Rng(16), Rng(17), Rng(18), Rng(19), Rng(20), _
            Rng(21), Rng(22), Rng(23), Rng(24), Rng(25), Rng(26), Rng(27), Rng(28), Rng(29), Rng(30))
                If Cnt2 = 29 Then
                Cnt2 = 0
                On Error Resume Next
                Set FinalRslt = Union(FinalRslt, Rslt(1), Rslt(2), Rslt(3), Rslt(4), Rslt(5), Rslt(6), Rslt(7), Rslt(8), Rslt(9), Rslt(10), _
                Rslt(11), Rslt(12), Rslt(13), Rslt(14), Rslt(15), Rslt(16), Rslt(17), Rslt(18), Rslt(19), Rslt(20), _
                Rslt(21), Rslt(22), Rslt(23), Rslt(24), Rslt(25), Rslt(26), Rslt(27), Rslt(28), Rslt(29))
                    If Err <> 0 Then
                    Debug.Print " Row: " & Rw & " at " & Timer - tm & " Error: " & Err.Number & vbCrLf & Err.Description
                    Err.Clear
                    On Error GoTo 0
                    Exit For
                    Else
                    Debug.Print " Row: " & Rw & " at "; Timer - tm
                    End If
                End If
            End If
         DoEvents
         End If
    Next
AreaCnt = FinalRslt.Areas.Count
Debug.Print "Completed at " & Timer - tm & " Row: " & Rw & " Range Area Count: " & AreaCnt
End Sub

The code still lacks final touches to complete union at end (if end of range reaches between accumulating 30 range array) but ignored as it is only for test purpose

Few extracts of the debug log

With contiguous area length 1  separated by 1 row 
Row: 129920 at  289.71875 
Row: 131080 at 289.765625 Error: 1004
Method 'Union' of object '_Global' failed
Completed at 289.78125 Row: 131080 Range Area Count: 32480

With contiguous area length 3  separated by 1 row 
Row: 129920 at  307.8359375 
 Row: 131080 at 307.8984375 Error: 1004
Method 'Union' of object '_Global' failed
Completed at 307.9140625 Row: 131080 Range Area Count: 32480

With contiguous area length 5  separated by 1 row, it completed 150k Row
Row: 147204 at  236.8046875 
Row: 148248 at  242.71875 
Row: 149292 at  248.2109375 
Completed at 248.2734375 Row: 150000 Range Area Count: 24882

With the test results, is it to conclude that Microsoft union method is incapable of creating an union range with non contiguous area count more than 32 K?

Of course both the methods of creating range from SpecialCells, union and Array range can be combined and or tweaked to many simple workarounds. But the final question is

Are we really bound by 8K non contiguous area count limitation of creating range from SpecialCells and 32 K non contiguous area count limitation of union Range?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.