VBAコーディングパターン:Range Loop - 読み取り(Read)

VBAコーディングパターンのうち、Range Loop-Read(Read)の内容を見てみましょう。

要約

Range Loopは、Excelシートに入力されているデータをVBAコードで読み取って処理するときによく使用されるコーディングパターンです。開始セルから最後のセルまで順次読み込む基本的なパターンと、順次読みずに一度に読み込む性能改善パターンを以下に紹介し、性能を比較する。

Test data, Test VBA Code

説明のために、例示データを10万行、10列、合計100万セルで生成し、各セルの値を(行、列)の座標値に設定した。

以下のgithubファイルLinkでは、例データ、「B2」セルを始点として10行(100セル)から10万行(100万セル)まで、Rangeを探索するコーディングパターンと実行結果を確認することができる。

ファイルリンク:github DAToolset/VBACode/VBA コーディング パターン-Range Loop(Read).xlsm

표1. Test Data
表1 Test Data

Range Loop-読み取り(Read) 基本パターン-Nested Loop

基本パターンは、開始セルに基づいて行と列のオフセットを増やしながら、行のLoop(繰り返し)構文内の列の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 단위로 읽고 처리하는 코드 예시
            'Debug.Print oBaseRange.Offset(lRowOffset, lColOffset).Value
            sVal = oBaseRange.Offset(lRowOffset, lColOffset).Text
        Next lColOffset
    Next lRowOffset
End Sub

内部For Loopの次のコードは、Cell値を読み取る最も簡単なコードを例として書いています。

            sVal = oBaseRange.Offset(lRowOffset, lColOffset).Text

単一ループを使用したパターン - Single Loop(1), Text を使用

列の内部反復文を列数と同じ文で置き換える構文で、詳細コードは次のとおりです。 Rangeの値を取り出すときにTextを使用しました。

'단일 Loop를 사용하는 패턴: Single Loop(1), Text  사용
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 = Range(oBaseRange, oBaseRange.End(xlDown)).Rows.Count
    lRowCount = aTargetRowCnt
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    For lRowOffset = 0 To lRowCount - 1 '읽는 행의 수 (Offset은 0부터 시작하므로 Count -1 까지 Loop 실행해야 함)
        sVal = oBaseRange.Offset(lRowOffset, 0).Text
        sVal = oBaseRange.Offset(lRowOffset, 1).Text
        sVal = oBaseRange.Offset(lRowOffset, 2).Text
        sVal = oBaseRange.Offset(lRowOffset, 3).Text
        sVal = oBaseRange.Offset(lRowOffset, 4).Text
        sVal = oBaseRange.Offset(lRowOffset, 5).Text
        sVal = oBaseRange.Offset(lRowOffset, 6).Text
        sVal = oBaseRange.Offset(lRowOffset, 7).Text
        sVal = oBaseRange.Offset(lRowOffset, 8).Text
        sVal = oBaseRange.Offset(lRowOffset, 9).Text
    Next lRowOffset
End Sub

単一ループを使用するパターン - Single Loop(2), Text の代わりに Value2 を使用

Single Loop構文を保持してRangeの値を取り出すときは、Textの代わりにValue2を使用しました。性能比較は一番下に書いておいた。

' 행만 Loop, Text 대신 Value2 사용
Public Sub SingleLoop(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 = Range(oBaseRange, oBaseRange.End(xlDown)).Rows.Count
    lRowCount = aTargetRowCnt
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    For lRowOffset = 0 To lRowCount - 1 '읽는 행의 수 (Offset은 0부터 시작하므로 Count -1 까지 Loop 실행해야 함)
        sVal = oBaseRange.Offset(lRowOffset, 0).Value2
        sVal = oBaseRange.Offset(lRowOffset, 1).Value2
        sVal = oBaseRange.Offset(lRowOffset, 2).Value2
        sVal = oBaseRange.Offset(lRowOffset, 3).Value2
        sVal = oBaseRange.Offset(lRowOffset, 4).Value2
        sVal = oBaseRange.Offset(lRowOffset, 5).Value2
        sVal = oBaseRange.Offset(lRowOffset, 6).Value2
        sVal = oBaseRange.Offset(lRowOffset, 7).Value2
        sVal = oBaseRange.Offset(lRowOffset, 8).Value2
        sVal = oBaseRange.Offset(lRowOffset, 9).Value2
    Next lRowOffset
End Sub

データを一度にメモリに読み込む方法 - Variant Array の使用

データを読み取る範囲を決め、2次元Variant Arrayで一度に読み取る方法で詳細コードは次のようになる。

' 한번에 읽는 패턴
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
    vRngArr = oBaseRange.Resize(aTargetRowCnt, lColCount).Value2
    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)
            'Debug.Print vRngArr(lRow, lCol)
            sVal = vRngArr(lRow, lCol)
        Next lCol
    Next lRow
End Sub

ここでキーは次のコードです。

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

Resizeは、開始範囲(Range)から指定した行、列の数だけ範囲を拡張して値を読み取る対象を設定する。 Value2は、Rangeの値を型なしで元の値に取り出す属性です。値を取り出す Range に複数の行と列が含まれている場合、その値は 2 次元 Variant Array で取り出されます。したがって、上記のコードは、読み取る範囲全体を一度にmemoryで読み取ることになる。

性能比較

10から10万までRow Countを増加させながら各方法の実行時間をまとめると次のようになる。
(実行環境:2019 LG Gram、CPU i5、Ram 16GB、256GB SSD)

표2. 입력 Count와 소요시간(초)
表2。入力Countと所要時間(秒)
그림1. 입력 Count와 소요시간(초)
図1。入力Countと所要時間(秒)

実行性能は次のように要約することができる。

  • Nested Loop、Single Loop 繰り返し構文自体の実行性能はほとんど差がない。
  • Rangeから値を取り出すときにTextを使用すると遅くなり、Value2を使用すると高速です。
  • Variant Arrayを使用すると、データが多くても非常に迅速に読み取ることができます。

ちなみに、Rangeから値を取り出すときに使用するText、Value、Value2属性の比較は、後で時間になると整理します。一度はgoogleで検索して見つけたstackoverflow.comの文をリンクしておく。
https://stackoverflow.com/questions/17359835/what-is-the-difference-between-text-value-and-value2

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

ja日本語