【Excel VBA】Range・Cells・Worksheetsの使い分け — セル指定は「ブック→シート→セル」の3階層で考える

この記事は約10分で読めます。

Range・Cells・Worksheets・Workbooksと種類が多くて迷う方へ。丸暗記ではなく、「ブック → シート → セル」の階層という1本の軸で整理します。

この記事で分かること

  • VBAのセル指定が「ブック → シート → セル」の3階層でできていること
  • 階層を省略すると、意図しないシートを書き換える事故が起きる理由
  • Range と Cells、シートの名前指定と番号(インデックス)指定の使い分け
  • With で階層をまとめる書き方

どんな場面で使うか

  • 複数のシートやブックをまたいで処理するマクロを書く
  • マクロの記録で作ったコードを、書き直したい
  • 「実行するたびに結果が違う」「別のシートに書き込まれた」を防ぎたい

基本説明

使うデータ

サンプルファイル(記事末尾)には、「東京」「大阪」「名古屋」の3シートがあります。

「東京」シートの元データ(A1:B6。A列=商品名、B列=売上、2〜6行目に5件)

どのシートも、A列が商品名、B列が売上です。データは2〜6行目の5件で、大阪・名古屋は値だけが違います。最後に、この3シートの売上を合計するマクロを作ります。

セルの指定は3階層でできている

「東京シートのB2」と言うとき、私たちはブック → シート → セルの順に絞り込んでいます。VBAも同じ順に書きます。

ThisWorkbook.Worksheets("東京").Range("B2").Value

.(ドット)は「の」と読むと分かりやすくなります。

位置 意味 上の例
1つめ どのブックの ThisWorkbook
2つめ どのシートの Worksheets("東京")
3つめ どのセルの Range("B2")
4つめ 何を .Value

Range・Cells・Worksheetsは別々の機能ではなく、この階層のどこを指すかの違いです。

省略すると「今アクティブなもの」が使われる

上の階層は省略できます。ただし標準モジュールでは、Excelが今アクティブなブックの、今アクティブなシートを補います。

Range("B2").Value = 100
' ↑ 実際は ActiveWorkbook.ActiveSheet.Range("B2") として実行される

別のブックを開いた状態で実行すると、そちらのB2を書き換えます。 エラーは出ません。「昨日は動いたのに」の多くはこれが原因です。

迷ったら、階層を省略せずに書きます。

マクロ記録が Select・Activate だらけになる理由

記録機能は、人のクリック操作をそのまま文字にします。そのため Range("B2").Select のように、「シートやセルを選んでから操作する」コードになります。

VBAでは、Worksheets("東京").Range("B2").Value のように階層で書けば、シートを切り替えずに読み書きできます。Select・Activate の比較は【Excel VBA】Select、Activate、他セル指定方法の比較で扱っています。

階層① ブック — ThisWorkbook を基本にする

  • ThisWorkbook … このマクロが書かれているブック。アクティブなブックが変わっても、指す先は変わらない
  • ActiveWorkbook … 今アクティブなブック。別のブックを開くと変わる
  • Workbooks("集計元.xlsx") … 開いている別のブックを、ファイル名(拡張子込み)で指定する

マクロが入っているブック自身に書くときは、ThisWorkbook を使います。

階層② シート — 名前で指定する

  • 名前指定 … Worksheets("東京")
  • 番号(インデックス)指定 … Worksheets(1)(何番目のシートか)

番号指定は、左側にシートを足したり、並びを動かしたりすると、別のシートを指すことがあります。 しかもエラーにならず、黙って別のシートを処理します。

名前指定は、シート名を変えられると実行時エラーで止まります。止まるので気づけます。特別な理由がなければ、名前指定を使います。

階層③ セル — Range と Cells

書き方 向いている場面
Range Range("B2")・Range("B2:B6") のように、Excelの表記のまま 決まったセル・範囲を指定する
Cells Cells(2, 2) のように、行番号・列番号の数値 ループで行や列を動かす
Worksheets("東京").Range("B2").Value   ' B2 をそのまま指定
Worksheets("東京").Cells(2, 2).Value   ' 2行目・2列目 = 同じB2

どちらもB2です。Cells は行番号が数値なので、Cells(i, 2) のように変数を入れられます。

With で階層をまとめる

同じシートを何度も書くときは、With でまとめます。

With ThisWorkbook.Worksheets("東京")
    MsgBox .Range("B2").Value & " / " & .Cells(3, 2).Value
End With

. で始まる部分は、With で指定したシートの続きとして読まれます。この例は、東京シートのB2とB3を読んで 100 / 200 と表示します。

With の内側で . を付け忘れると、省略した書き方に戻ります。

手順

VBEにコードを書く

  1. Alt + F11 でVBE(Visual Basic Editor)を開く
  2. 「挿入」→「標準モジュール」を選ぶ
  3. モジュールの1行目に Option Explicit が最初から入っていたら、消しておく(下のステップ1のコードの先頭にも入っていて、1行目の1つだけで足りるため)
  4. 下の「サンプルコード」のステップ1〜3を、順に貼り付ける
  5. 左側のプロジェクトエクスプローラーで、東京・大阪・名古屋の3シートが並んでいることを確かめる

VBEのプロジェクトエクスプローラーとコード画面

Worksheets("東京") と名前で書いたシートが、このツリーに並ぶ実物です。

実行して確認する

  1. 実行したい Sub の中をクリックする
  2. F5 を押す
  3. メッセージボックスで結果を確かめる

サンプルコード

3段階に分けて積み上げます。3つとも同じ標準モジュールに貼ります。Option Explicit は1行目の1つだけです。

ステップ1:1つのセルを階層つきで読む

Option Explicit

Sub 東京のB2を読む()
    MsgBox ThisWorkbook.Worksheets("東京").Range("B2").Value
End Sub

100 と表示されます。大阪シートをアクティブにして実行しても、結果は変わりません。 階層を書いているためです。

ステップ2:1シートの売上を合計する

Sub 東京の売上を合計する()
    Dim r As Long
    Dim total As Double

    With ThisWorkbook.Worksheets("東京")
        For r = 2 To 6
            total = total + .Cells(r, 2).Value
        Next r
    End With

    MsgBox "東京 : " & total
End Sub

「東京 : 1000」と表示されます。Cells(r, 2) なら、r が2→6と変わるだけで下の行へ進めます。

ステップ3:3シートを横断して合計する

Sub 支店別売上を合計する()
    Dim shopNames As Variant
    shopNames = Array("東京", "大阪", "名古屋")

    Dim shopName As Variant
    Dim r As Long
    Dim lastRow As Long
    Dim shopTotal As Double
    Dim grandTotal As Double
    Dim msg As String

    For Each shopName In shopNames
        shopTotal = 0
        With ThisWorkbook.Worksheets(shopName)
            ' B列の最終行を取得する(データが増減しても追従する)
            lastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
            For r = 2 To lastRow
                shopTotal = shopTotal + .Cells(r, 2).Value
            Next r
        End With
        msg = msg & shopName & " : " & shopTotal & vbCrLf
        grandTotal = grandTotal + shopTotal
    Next shopName

    MsgBox msg & vbCrLf & "合計 : " & grandTotal, vbInformation, "支店別売上"
End Sub

ステップ2から変わったのは2か所です。

  • シート名を変数で渡す … Worksheets(shopName)。対象を増やすときは、Array に店舗名を足すだけ
  • 最終行を自動で求める … .Cells(.Rows.Count, "B").End(xlUp).Row は、B列の一番下から Ctrl + ↑ を押す操作と同じ。行数が増えても追いつく

動作確認

サンプルファイル(記事末尾)を開き、ステップ1〜3の Sub を貼った状態で確かめます。途中の短いコードは説明用なので、実行しなくて構いません。

  1. Alt + F11 でExcelの画面に戻り、「東京」シートのタブをクリックする
  2. Alt + F11 でVBEに戻り、東京のB2を読む の中をクリックして F5 → 100 と表示されれば成功
  3. 同じようにExcelの画面で「大阪」シートのタブをクリックしてから、東京のB2を読む をもう一度 F5 で実行する → 100 のままなら成功(アクティブなシートに左右されない)
  4. 支店別売上を合計する の中をクリックして F5 → 「東京 : 1000」「大阪 : 500」「名古屋 : 350」「合計 : 1850」と表示されれば成功

よくあるエラー

実行時エラー「9」(インデックスが有効範囲にありません)

原因: Worksheets("東京") のシート名が実在しません。全角・半角の違いや、名前の前後の空白もよくある原因です。

直し方: シートのタブの名前と、コードの "" の中を1文字ずつ見比べます。

別のシートが書き換わっていた(エラーは出ない)

原因: With の内側で . を付け忘れています。Range("B2") はアクティブシートのB2を指します。

直し方: With の内側は、.Range・.Cells のように . から書き始めます。

Worksheets(1) で書いたら、後で別のシートを処理していた

原因: シートを足したり動かしたりして、並び順が変わりました。

直し方: Worksheets("東京") のような名前指定に書き換えます。

実行時エラー「13」(型が一致しません)

原因: 合計するB列に、「未入力」「−」などの文字が混ざっています。

直し方: 数値以外を飛ばすなら、足し算の行を If IsNumeric(.Cells(r, 2).Value) Then 〜 End If で囲みます。

まとめ

  • セル指定は「どのブックの → どのシートの → どのセル」の階層で読む
  • 上の階層を省略すると、今アクティブなものが使われる。エラーにならないので気づきにくい
  • 基本は ThisWorkbook とシート名から書き始める
  • 決まった範囲なら Range、ループで動かすなら Cells
  • 同じ階層を繰り返すなら With。内側は . から書く

関連記事

サンプルファイル

記事中で使用しているサンプルデータはこちらからダウンロードできます。

タイトルとURLをコピーしました