要点(TL;DR) — ワークシートには 3つの取っ手 があり、安全なのは1つだけです。
Sheets("Jan")は タブ名 を使います — ユーザーがリネームすればマクロは 「インデックスが有効範囲にありません」で落ちます。Sheets(1)は 位置 を使います — タブをドラッグすればマクロは黙って別のシートに書き込みます。Sheet1(VBEで設定する コードネーム)は、Excel上からユーザーが変更できない唯一の取っ手なので、壊れません。 最初に参照をつかみ(Set ws = ThisWorkbook.Worksheets("Data"))、.Select/.Activateを やめましょう。
ワークシートはワークブックの1つ下の階層です — ブックがシートを抱え、シートがセル範囲を抱えます。 繰り返し起きる痛みはシートを 使う ことではなく、シートに 名前を付ける ことです。参照を間違えると、 マクロは派手に落ちるか、もっと悪い場合は1つもエラーを出さずに間違ったタブに書き込みます。
この記事でわかること
- シートを参照する3つの方法と、裏切ってくる2つ
- なぜコードネームが鉄壁の取っ手なのか(とどこで設定するか)
SheetsとWorksheetsの違い(同義語ではありません)- なぜ
.Selectと.Activateはマクロ記録の癖で、本物のコードではないのか
考え方の軸:3つの取っ手、うち2つは動く
1枚のシートは3通りの方法でつかめる1つのオブジェクトです。そのうち2つの取っ手は ユーザー が 握っているもの — タブ名とタブの並び順 — に結びついているので、あなたの足元から動きます。 3つ目のコードネームは、VBEから あなただけ が握れるものに結びついています。
Sub ThreeWaysToGrabASheet()
' 1) タブ名で — ユーザーがリネームできる → 壊れる
Debug.Print Worksheets("Jan").Name
' 2) インデックス(位置)で — ユーザーがドラッグで並べ替えできる → 黙って別シート
Debug.Print Worksheets(1).Name
' 3) コードネームで — VBEで設定、ユーザーには見えない → 鉄壁
Debug.Print Sheet1.Name ' "Sheet1" はコードネームで、タブの文字ではない
End Sub
なぜ重要か: 取っ手#1と#2はユーザーの行動に依存します。誰かが「Jan」を「January」に変える、 あるいは「Feb」の後ろにドラッグする。するとマクロは 実行時エラー9(インデックスが有効範囲に ありません) を投げるか — もっと危険なことに — 走り続けて間違ったタブを編集します。 取っ手#3は免疫があります。コードネームはタブではなくコードの中に住んでいるからです。
ルール:コードネームを設定する、または参照を一度だけつかむ
失敗モードは、マクロの奥深くで「動く取っ手」を使うことです。解決策は、先頭で 一度だけ シートを 固定し、あとはその変数だけに話しかけることです。
Sub SafeSheetReference()
' 参照を一度だけつかむ — ブックまで完全限定
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Data")
' ここから先はすべて ws 経由。「アクティブ」なし、再検索なし。
ws.Range("A1").Value = "Report"
ws.Range("A2:A100").ClearContents
Debug.Print ws.UsedRange.Rows.Count
End Sub
名前自体を壊れなくするには、シートにコードネームを付けます:VBEで Microsoft Excel Objects の
下のシートを選び、プロパティウィンドウ(F4)を開き、(Name) を設定します。これで
Set ws = SalesData と書け、Excelでタブをリネームしても何も変わりません。判断:他人がそのファイルを
使っても生き残るべきマクロなら、シートはコードネームで参照してください — タブ名はコードではなく
ユーザーのコンテンツです。
Sheets vs Worksheets — 同じコレクションではない
これはほぼ全員がつまずきます。Worksheets は グリッドのシートだけ です。Sheets は
タブを持つすべて — グラフシートを含む です。間違ったほうをループすると、.Range を持たない
グラフシートに当たって落ちます。
Sub SheetsVsWorksheets()
' Worksheets:グリッドのシートだけ — .Range / .Cells を触っても安全
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Range("A1").Value = "Audited"
Next ws
' Sheets:グリッド + グラフシート — グラフシートで .Range = 落ちる
Debug.Print ThisWorkbook.Worksheets.Count ' グリッドのみ
Debug.Print ThisWorkbook.Sheets.Count ' グリッド + グラフ
End Sub
目安:ループがセルを触るなら Worksheets を回し、Sheets は使わない。グラフシートが本当に
必要なときだけ Sheets に手を伸ばします。
.Select と .Activate をやめる
マクロ記録は Sheets("Data").Select の次に Range("A1").Select と書きます。あなたが クリックした
ことしか記録できないからです。本物のコードはクリックそのものを飛ばします — シートオブジェクトに
直接話しかけます。選択は遅く、画面をちらつかせ、(ActiveSheetを変えるので)まさに上で見た
別シートバグの下地を作ります。
' 記録スタイル — 選択してから選択範囲に対して動く(もろい)
Sheets("Data").Select
Range("A1").Select
Selection.Value = 100
' 本物のコード — 1行、選択なし、ActiveSheet依存なし
ThisWorkbook.Worksheets("Data").Range("A1").Value = 100
シートを読み書きするのに選択はほぼ不要です。そもそもなぜ「アクティブ」が罠なのかは VBA ActiveSheet を参照してください。
ExcelMasterでの代替
複数シートを扱うVBAの多くは、定期的にタブ間でデータを動かすために存在します。ExcelMaster は それを自然言語の説明から行います — 「Dataの整形済み行をSummaryタブにコピーして合計を更新して」 — 名前を間違えるシート参照なしで。「インデックスが有効範囲にありません」系のバグまるごとを 飛ばせます。
常駐の組み込み自動化にはVBAがこれからも活きます。ですが日常の「このタブからあのタブへ移す」なら、 言葉で説明するほうが速く、誰かがシートをリネームしても壊れません。
よくある質問
ワークシートを参照すると「インデックスが有効範囲にありません」が出るのはなぜ?
渡したタブ名が存在しないからです — 多くはシートのリネーム、末尾の空白、タイプミスが原因。 代わりに(VBEで設定する)コードネームで参照するか、検索を包んでシートの存在を先に確認してください。
ワークシートのコードネームとは?どこで設定する?
コードネームはVBAが使う内部名(Sheet1、Sheet2 …)で、VBEのプロジェクトエクスプローラーに
表示されます。プロパティウィンドウ(F4)→ (Name) で設定します。タブ名と違い、ユーザーはExcelから
変更できないので、最も信頼できる取っ手です。
VBAのSheetsとWorksheetsの違いは?
Worksheets は標準のグリッドシートだけを含みます。Sheets はグラフシートを含むすべての種類の
シートを含みます。コードが .Range や .Cells を使うなら Worksheets をループしてください
— グラフシートにはセルがなく、落ちます。
シートを編集するのにSelectやActivateは必要?
いいえ。ThisWorkbook.Worksheets("Data").Range("A1").Value = 1 は何も選択せずに動きます。
.Select/.Activate はマクロ記録の名残で、マクロを遅くし、別シートバグを生みます。
検証環境
検証環境: Excel 365(Windows 11)、VBA 7.1 — 最終確認 2026-06-13。
関連ガイド: VBA Workbook · VBA ActiveSheet · VBA Range · VBA For Loop · VBA Dictionary
