VBA Coding Pattern: Range Loop - Write

Let's look at the contents of Range Loop-Write among VBA coding patterns.

summary

Introduces a coding pattern that improves performance when writing a lot of data in an Excel sheet using Variant Array.

How to improve performance when reading a lot of data from an Excel sheet has already been covered in the article below.

VBA Coding Pattern: Range Loop - Read

One-line summary: Reading from a Variant Array is incomparably faster than a Loop.

This time, we will look at how to write data to an Excel sheet from within VBA code. The performance of each method is compared at the bottom.

test method

Run the test in the following way.

  • Values are written to each cell while increasing rows and columns based on Range(“B2”).
  • The value recorded in the cell is in the format of “row number, column number”.
  • The number of columns is set to fixed (10 columns).
  • Repeat while increasing the number of rows from 10, 100, 500, 1000 to 100,000.
  • Since the number of cells is the number of rows x 10, we test up to 1 million cells.

The test code can be downloaded from github.

github file link: DAToolset/VBACode/VBA Coding Patterns - Range Loop(Write).xlsm

Basic Pattern - Nested Loop

The basic pattern uses the Loop statement for the column inside the Loop statement for the row while increasing the offset of the row and column based on the starting cell.

The detailed code is as follows. previous post VBA Coding Pattern: Range Loop - Read and the code is almost the same, and read is changed to write. (line 10)

Since the Text of the Range object is a read-only property, a value cannot be entered, so the value is entered directly into the Range object.

'* 기본 패턴(Nested Loop)
Public Sub NestedLoop(aTargetRowCnt As Long)
    Dim lRowOffset As Long, lColOffset As Long, oBaseRange As Range, sVal As String
    Set oBaseRange = Range("B2")
    lRowOffset = 0
    For lRowOffset = 0 To aTargetRowCnt '읽는 행의 수(충분히 큰 수로 지정하고 Loop 내부에서 종료 조건 설정)
        If Trim(oBaseRange.Offset(lRowOffset, 0).Value) = "" Then Exit For 'Loop 종료 조건
        For lColOffset = 0 To 9 '읽는 열의 수
            'cell 단위로 쓰는 코드 예시
            oBaseRange.Offset(lRowOffset, lColOffset) = CStr(lRowOffset + 1) + "," + CStr(lColOffset + 1)
        Next lColOffset
    Next lRowOffset
End Sub

Patterns using a single Loop - Single Loop(1)

The internal loop statement for the columns was replaced with as many statements as the number of columns (lines 11 to 20). Similarly, we entered values directly into the Range object.

'단일 Loop를 사용하는 패턴: Single Loop(1)
Public Sub SingleLoop1(aTargetRowCnt As Long)
    Dim lRowOffset As Long, lColOffset As Long, oBaseRange As Range, sVal As String
    Set oBaseRange = Range("B2")
    lRowOffset = 0
    Dim lRowCount As Long, lColCount As Long, oHeaderRange As Range
    Set oHeaderRange = oBaseRange.Offset(-1, 0)
    lRowCount = aTargetRowCnt
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    For lRowOffset = 0 To lRowCount - 1 '읽는 행의 수 (Offset은 0부터 시작하므로 Count -1 까지 Loop 실행해야 함)
        oBaseRange.Offset(lRowOffset, 0) = CStr(lRowOffset + 1) + ",1"
        oBaseRange.Offset(lRowOffset, 1) = CStr(lRowOffset + 1) + ",2"
        oBaseRange.Offset(lRowOffset, 2) = CStr(lRowOffset + 1) + ",3"
        oBaseRange.Offset(lRowOffset, 3) = CStr(lRowOffset + 1) + ",4"
        oBaseRange.Offset(lRowOffset, 4) = CStr(lRowOffset + 1) + ",5"
        oBaseRange.Offset(lRowOffset, 5) = CStr(lRowOffset + 1) + ",6"
        oBaseRange.Offset(lRowOffset, 6) = CStr(lRowOffset + 1) + ",7"
        oBaseRange.Offset(lRowOffset, 7) = CStr(lRowOffset + 1) + ",8"
        oBaseRange.Offset(lRowOffset, 8) = CStr(lRowOffset + 1) + ",9"
        oBaseRange.Offset(lRowOffset, 9) = CStr(lRowOffset + 1) + ",10"
    Next lRowOffset
End Sub

Patterns using a single Loop - Single Loop(2)

Value2 was used when maintaining the Single Loop syntax and entering values into the Range.

'단일 Loop를 사용하는 패턴: Single Loop(2), Value2 사용
Public Sub SingleLoop2(aTargetRowCnt As Long)
    Dim lRowOffset As Long, lColOffset As Long, oBaseRange As Range, sVal As String
    Set oBaseRange = Range("B2")
    lRowOffset = 0
    Dim lRowCount As Long, lColCount As Long, oHeaderRange As Range
    Set oHeaderRange = oBaseRange.Offset(-1, 0)
    lRowCount = aTargetRowCnt
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    For lRowOffset = 0 To lRowCount - 1 '읽는 행의 수 (Offset은 0부터 시작하므로 Count -1 까지 Loop 실행해야 함)
        oBaseRange.Offset(lRowOffset, 0).Value2 = CStr(lRowOffset + 1) + ",1"
        oBaseRange.Offset(lRowOffset, 1).Value2 = CStr(lRowOffset + 1) + ",2"
        oBaseRange.Offset(lRowOffset, 2).Value2 = CStr(lRowOffset + 1) + ",3"
        oBaseRange.Offset(lRowOffset, 3).Value2 = CStr(lRowOffset + 1) + ",4"
        oBaseRange.Offset(lRowOffset, 4).Value2 = CStr(lRowOffset + 1) + ",5"
        oBaseRange.Offset(lRowOffset, 5).Value2 = CStr(lRowOffset + 1) + ",6"
        oBaseRange.Offset(lRowOffset, 6).Value2 = CStr(lRowOffset + 1) + ",7"
        oBaseRange.Offset(lRowOffset, 7).Value2 = CStr(lRowOffset + 1) + ",8"
        oBaseRange.Offset(lRowOffset, 8).Value2 = CStr(lRowOffset + 1) + ",9"
        oBaseRange.Offset(lRowOffset, 9).Value2 = CStr(lRowOffset + 1) + ",10"
    Next lRowOffset
End Sub

How to write data in memory to Range at once - Using Variant Array

This is a method of writing data stored in a two-dimensional variant array to a range at once. The detailed code is as follows.

' 한번에 쓰는 패턴
Public Sub VariantArray(aTargetRowCnt As Long)
    Dim vRngArr As Variant, oBaseRange As Range, oHeaderRange As Range, lColCount As Long, sVal As String
    Set oBaseRange = Range("B2")
    Set oHeaderRange = oBaseRange.Offset(-1, 0)
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    ReDim vRngArr(1 To aTargetRowCnt, 1 To lColCount)
    Dim lCol As Long, lRow As Long
    For lRow = LBound(vRngArr, 1) To UBound(vRngArr, 1)
        For lCol = LBound(vRngArr, 2) To UBound(vRngArr, 2)
            vRngArr(lRow, lCol) = CStr(lRow) + "," + CStr(lCol)
        Next lCol
    Next lRow
    oBaseRange.Resize(aTargetRowCnt, lColCount).Value2 = vRngArr
End Sub

The core code is line 14. Write data by directly assigning a Variant array to Range.

    oBaseRange.Resize(aTargetRowCnt, lColCount).Value2 = vRngArr

VBA Coding Pattern: Range Loop - Read Comparing with the following code read from

    vRngArr = oBaseRange.Resize(aTargetRowCnt, lColCount).Value2

Performance Comparison

Range Write Performance Comparison

VBA Coding Pattern: Range Loop - Read Similarly, the execution time of each method was summarized while increasing the Row Count from 10 to 100,000.
(Execution environment: 2019 LG Gram, CPU i5, Ram 16GB, 256GB SSD)

표1. Row Count, Cell Count와 각 방법의 쓰기(Write) 소요시간(초)
Table 1. Row Count, Cell Count, and the write time required for each method (seconds)
그림1. Row Count, Cell Count와 각 방법의 쓰기(Write) 소요시간(초)
Figure 1. Row Count, Cell Count, and the write time required for each method (seconds)

Range write performance can be summarized as follows.

  • There is almost no difference between the method of entering a value using Value2 and the method of entering a value directly into the Range object.
  • Nested Loop was slightly faster than Single Loop. This is the opposite of Read, so it's hard to say that it's always faster.
  • If you write data using Variant Array, it is incomparably faster (at least 10 times or more).

Range read performance comparison

For immediate comparison of read and write performance Read performance comparison Copy the contents below.

표2. Row Count, Cell Count와 각 방법의 읽기(Read) 소요시간(초)
Table 2. Row Count, Cell Count, and the read time required for each method (seconds)
그림1. Row Count, Cell Count와 각 방법의 읽기(Read) 소요시간(초)
Figure 1. Row Count, Cell Count, and the read time required for each method (seconds)

When reading and writing a large amount of data in an Excel sheet with VBA code, performance can be greatly improved by using a variant array, so please use it appropriately if necessary.

Leave a Reply

Your email address will not be published. Required fields are marked *

en_USEnglish