如何在VBA中高效将变量值添加到组合范围?

huangapple go评论51阅读模式
英文:

How can I efficiently add a variable value to a combined range in VBA?

问题

在VBA中向组合范围添加值

我正在尝试将一个变量值添加到一个包含多个范围的范围中。使用 "for each cell" 非常慢,需要很长时间。当范围不包含多个范围时,我成功地使用了这段代码,现在,在使用一组范围的组合时,宏会在没有错误的情况下完成 - 但查看我试图更改的单元格时,会出现 #VALUE 错误。

Sub rangecheck()

Dim x As Long
Dim number_of_tenants As Long
Dim range_names() As String
Dim ranges() As Range
Dim dynamic_range As Name

number_of_tenants = 5
ERV_change = 0.1

ReDim range_names(1 To number_of_tenants)
ReDim ranges(1 To number_of_tenants)

    For x = 1 To number_of_tenants
    range_names(x) = "range" & x
    Set ranges(x) = Range("F" & 21 + 172 * (x - 1) & ":AJ" & 21 + 172 * (x - 1))
    Set dynamic_range = ThisWorkbook.Names.Add(Name:=range_names(x), RefersTo:=ranges(x))
    Next x

Set combined_range = Range("range1")
    For x = 2 To number_of_tenants
        Set current_range = Range("range" & x)
        Set combined_range = Union(combined_range, current_range)
    Next x
    
combined_range.Value = Evaluate(combined_range.Address & "+" & CStr(ERV_change))

End Sub

感谢任何帮助。

英文:

Adding a value to a combined range in VBA

I am trying to add a variable value to a range, which consist of multiple ranges. Using "for each cell" is super slow and takes forever. I managed to use this code successfully when the range is not consisting of multiple ranges, now, using a combination of ranges, the macro finishes without errors - but looking at the cells I am trying to change, there is a #VALUE error.

Sub rangecheck()

Dim x As Long
Dim number_of_tenants As Long
Dim range_names() As String
Dim ranges() As Range
Dim dynamic_range As Name

number_of_tenants = 5
ERV_change = 0.1

ReDim range_names(1 To number_of_tenants)
ReDim ranges(1 To number_of_tenants)

    For x = 1 To number_of_tenants
    range_names(x) = "range" & x
    Set ranges(x) = Range("F" & 21 + 172 * (x - 1) & ":AJ" & 21 + 172 * (x - 1))
    Set dynamic_range = ThisWorkbook.Names.Add(Name:=range_names(x), RefersTo:=ranges(x))
    Next x

Set combined_range = Range("range1")
    For x = 2 To number_of_tenants
        Set current_range = Range("range" & x)
        Set combined_range = Union(combined_range, current_range)
    Next x
    
combined_range.Value = Evaluate(combined_range.Address & "+" & CStr(ERV_change))

End Sub

Thanks for any help.

答案1

得分: 1

正如@TimWilliams提到的,以下部分速度非常快:在我的机器上,对于5000个租户,花费了2.94秒,而您的代码花费了109.52秒。此外,在取消注释命名行时,花费了9.09秒。

Sub rangecheck()
  Dim start As Double: start = Timer
  
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  
  Dim x As Long
  Dim number_of_tenants As Long:  number_of_tenants = 5
  Dim ERV_change As Double:       ERV_change = 0.1
  Dim row_offset As Long:         row_offset = 21
  Dim sht As Worksheet:           Set sht = ThisWorkbook.Sheets("enterSheetNameHere")
  
  For x = 1 To number_of_tenants
    With sht.Range("F" & row_offset & ":AJ" & row_offset)
      .Value = Evaluate("=" & sht.Name & "!" & .Address & "+" & ERV_change)
      '* 如果您必须为范围命名,则取消注释以下行
      '*.Name = "range" & x
    End With
    row_offset = row_offset + 172
  Next x
  
  Application.ScreenUpdating = True
  Application.EnableEvents = True

  MsgBox "Time taken: " & Format(Timer - start, "0.0000")
End Sub
英文:

As @TimWilliams mentioned the following is pretty fast: On my machine it took 2.94 seconds for 5000 tenants, whereas your code took 109.52 seconds. Furthermore, it took 9.09 seconds when the naming line is uncommented.

Sub rangecheck()
  Dim start As Double: start = Timer
  
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  
  Dim x As Long
  Dim number_of_tenants As Long:  number_of_tenants = 5
  Dim ERV_change As Double:       ERV_change = 0.1
  Dim row_offset As Long:         row_offset = 21
  Dim sht As Worksheet:           Set sht = ThisWorkbook.Sheets("enterSheetNameHere")
  
  For x = 1 To number_of_tenants
    With sht.Range("F" & row_offset & ":AJ" & row_offset)
      .Value = Evaluate("=" & sht.Name & "!" & .Address & "+" & ERV_change)
      '* If you have to name the ranges then uncomment the following line
      '.Name = "range" & x
    End With
    row_offset = row_offset + 172
  Next x
  
  Application.ScreenUpdating = True
  Application.EnableEvents = True

  MsgBox "Time taken: " & Format(Timer - start, "0.0000")
End Sub

答案2

得分: 0

我提议对您的代码进行以下更正:

Option Explicit

Public Sub rangecheck()
    Dim ERV_change As Double: ERV_change = 0.1
    Dim number_of_tenants As Long: number_of_tenants = 5
    Dim range_names() As String: ReDim range_names(1 To number_of_tenants)
    Dim ranges() As Range: ReDim ranges(1 To number_of_tenants)

    Dim x As Long: For x = 1 To number_of_tenants
        range_names(x) = "range" & x
        Set ranges(x) = Range("F" & 21 + 172 * (x - 1) & ":AJ" & 21 + 172 * (x - 1))
        Dim dynamic_range As Name: Set dynamic_range = ThisWorkbook.Names.Add(Name:=range_names(x), RefersTo:=ranges(x))
    Next x

    Dim combined_range As Range: Set combined_range = Range("range1")
    For x = 2 To number_of_tenants
        Dim current_range As Range: Set current_range = Range(range_names(x))
        Set combined_range = Union(combined_range, current_range)
    Next x

    Dim a As Long: For a = 1 To combined_range.Areas.Count
        Dim temp() As Variant: temp = combined_range.Areas(a).Value
        Dim r As Long: For r = LBound(temp, 1) To UBound(temp, 1)
            Dim c As Long: For c = LBound(temp, 2) To UBound(temp, 2)
                temp(r, c) = Val(temp(r, c)) + ERV_change
            Next c
        Next r
        combined_range.Areas(a).Value = temp
    Next a
End Sub

请注意,Range 对象具有一个 Areas 属性,该属性由多个 Range 对象组成,如果所涉及的 Range 是使用 Union 函数创建的,就像 combined_range 一样。每个 AreaValue 都是一个二维数组,可以一次检索和重写。

如果您尝试简单地读取或写入 combined_range.Value,这等同于读取或写入 combined_range.Areas(1).Value

因此,您必须遍历此 Area 属性。但是,在其中的每个区域中,所有单元格都将一次性在VBA和Excel之间进行读写,因此在这种情况下,这将产生最佳的性能,优于您可能希望避免的“对每个单元格”的解决方案。

英文:

I propose the following corrections to your code:

Option Explicit

Public Sub rangecheck()
    Dim ERV_change As Double: ERV_change = 0.1
    Dim number_of_tenants As Long: number_of_tenants = 5
    Dim range_names() As String: ReDim range_names(1 To number_of_tenants)
    Dim ranges() As Range: ReDim ranges(1 To number_of_tenants)
    
    Dim x As Long: For x = 1 To number_of_tenants
        range_names(x) = "range" & x
        Set ranges(x) = Range("F" & 21 + 172 * (x - 1) & ":AJ" & 21 + 172 * (x - 1))
        Dim dynamic_range As Name: Set dynamic_range = ThisWorkbook.Names.Add(Name:=range_names(x), RefersTo:=ranges(x))
    Next x
    
    Dim combined_range As Range: Set combined_range = Range("range1")
    For x = 2 To number_of_tenants
        Dim current_range As Range: Set current_range = Range(range_names(x))
        Set combined_range = Union(combined_range, current_range)
    Next x
    
    Dim a As Long: For a = 1 To combined_range.Areas.Count
        Dim temp() As Variant: temp = combined_range.Areas(a).Value
        Dim r As Long: For r = LBound(temp, 1) To UBound(temp, 1)
            Dim c As Long: For c = LBound(temp, 2) To UBound(temp, 2)
                temp(r, c) = Val(temp(r, c)) + ERV_change
            Next c
        Next r
        combined_range.Areas(a).Value = temp
    Next a
End Sub

Note that the Range object has an Areas property, which consists of multiple Range objects, if the Range in question was created with the Union function as in the case of combined_range. The Value of each of these is a two dimensional array, which can be retrieved and overwritten at once.

If you try to simply read or write combined_range.Value that is the same as reading or writing combined_range.Areas(1).Value.

So you have to iterate over this Area property. However, inside each of these, all cells will be written with one call between VBA and Excel, therefore this will produce the best available performance in this situation, better than the "for each cell" solution you understandably want to avoid.

huangapple
  • 本文由 发表于 2023年5月29日 04:21:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76353457.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定