TL;DR —
Sheets.Existsはありません。関数を一つ書いてすべてのプロジェクトに入れ、その場しのぎの書き方はやめましょう。一行だけエラー処理を切ってシートをSetし、変数がNothingかどうかを確かめます。三つの点を押さえれば、この関数は決して嘘をつきません。たまたまアクティブなブックではなく 特定のブック に問い合わせること。グラフシートがすでにその名前を使っていることがあるので、WorksheetsではなくSheetsを検索すること。そして、数値を入れたVariantは 位置 として読まれるので、名前をStringで渡すことです。非表示のシートも存在していることを忘れないでください — 「存在する」は「見えている」ではありません。
Function SheetExists(ByVal sheetName As String, Optional wb As Workbook) As Boolean
Dim sh As Object
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sh = wb.Sheets(sheetName)
On Error GoTo 0
SheetExists = Not sh Is Nothing
End Function
これは、シートの「身元」を扱う三部作の第三回です。シート見出しの名前は、ユーザーが持ち主のラベルです。リネーム はそのラベルを変え、非表示 はシートに手が届く状態のまま見出しを画面から消し、ここで扱う 存在確認 は、ラベルを信用する前に実行するチェックです。ユーザーが変えられる名前でシートを探すコードは、まずこの問いを投げるべきです。
この記事で学べること
- 考え方の軸:存在とは、一つのブックの中の一つの名前についての問い
- 関数と、その各行がある理由
- よくある書き方が間違った答えを返す三つのパターン
- ループ、On Error、Evaluate:どの方法をいつ使うか
- この確認を土台にするパターン:取得か作成、あれば削除、重複しない名前
- 閉じているブックのシートを確認する
考え方の軸:一つの名前、一つのブック、一つの名前空間
「シートは存在するか?」は、いつももっと狭い問いの省略形です。このブックに、種類を問わず、この文字列と大文字小文字を区別せずに一致する名前のシートがあるか? この文の各部分が、それぞれチェックが間違いうる場所です。このブック — うっかりアクティブなブックではなく。種類を問わず — ワークシートとグラフシートは一つの名前の集合を共有しています。大文字小文字を区別せずに — Excel にとって SALES と Sales は同じ名前です。
そして、存在するシートが画面に出ているとは限りません。非表示と完全非表示のシートもコレクションの中にあるので、チェックはそれを見つけます — その名前でシートを追加しようとしている場面では、まさにそれが正解です。ユーザーには見えなくても、Excel は重複を拒否するからです。
関数を一行ずつ
上の関数は四つのことをしており、どれも意味があります。
Dim sh As Object—WorksheetではなくObjectにすることで、グラフシートを代入しても型が一致しないエラーになりません。If wb Is Nothing Then Set wb = ThisWorkbook— 限定しないSheetsが暗黙に使うActiveWorkbookではなく、意図して選んだ既定値です。On Error Resume Next...On Error GoTo 0— エラーを無視するのはちょうど一行だけです。名前がないとき検索はエラー 9 を起こし、それこそが欲しい答えです。それ以外の場所のエラーは見えたままです。このスイッチを必ず元に戻さなければならない理由は On Error のガイド で説明しています。SheetExists = Not sh Is Nothing— 検索が失敗したなら、shは一度も設定されずNothingのままです。
名前がコードの外から来る場面 — セル、ユーザー、ファイル名 — では必ずこれを使いましょう。
If SheetExists("Summary") Then
ThisWorkbook.Sheets("Summary").Range("A1").Value = Date
Else
MsgBox "Summary シートが見つかりません。"
End If
Debug.Print SheetExists("Rates", Workbooks("Prices.xlsx")) ' 開いている別のブックに問い合わせる
よくある書き方が間違った答えを返す三つのパターン
1. 違うブックに問い合わせる。 多くのコード例は、前にブックを付けずに Sheets(name) を呼んでいます。これはその行が実行された時点でアクティブなブックを検索します — マクロが二つ目のファイルを開いた瞬間から、それはあなたが意図したブックではありません。チェックは「ない」と言い、次の行は違うブックにシートを追加し、エラーは何も出ません。この関数のように、ブックは必ず明示的に渡すか既定値を決めておきましょう。
2. Sheets ではなく Worksheets を検索する。 Worksheets にはワークシートしか入っていませんが、Sheets にはグラフシートも入っています。名前は 両方 を通して一意です。ですから、グラフシートの名前が Summary なら、Worksheets に対するチェックはその名前が空いていると答え、続く ws.Name = "Summary" はエラー 1004 で失敗します。「この名前を使えるか?」を問うなら、正しいコレクションは Sheets だけです。
3. うっかり数値を渡す。 Sheets(3) は三番目のシート、Sheets("3") は 3 という名前のシートです。名前が Variant で届き — たとえばセルから — そのセルに数値の 3 が入っていると、検索は位置で行われ、三番目にあるシートを何でも見つけてしまいます。チェックは、名前とは何の関係もないシートについて「存在する」と答えるのです。引数を ByVal sheetName As String と宣言すれば、検索の前に文字列への変換が強制されます。シグネチャが重要なのはそのためです。
ループ、On Error、Evaluate
三つの方法を目にするはずです。比べるとこうなります。
| 方法 | やり方 | 判定 |
|---|---|---|
On Error + Set |
検索を試し、Nothing かを確かめる |
既定 — 検索は一回、Excel とまったく同じ判定 |
ループ + StrComp |
各名前を大文字小文字を区別せずに比較する | わかりやすい。名前ではなくパターンで照合するときに使う |
Evaluate("ISREF(...)") |
数式の参照を組み立てて判定する | 避ける — アポストロフィとグラフシートで壊れる |
「存在する」の意味が本当は「名前が 2026 で始まるシートがあるか」なら、ループが正しい道具です。
Function FirstSheetLike(wb As Workbook, ByVal pattern As String) As Object
Dim sh As Object
For Each sh In wb.Sheets
If LCase$(sh.Name) Like LCase$(pattern) Then
Set FirstSheetLike = sh
Exit Function
End If
Next sh
End Function
' Set sh = FirstSheetLike(ThisWorkbook, "2026-*") ' 一致するものがなければ Nothing
Evaluate の小技 — Evaluate("ISREF('" & name & "'!A1)") — は賢そうに見えますが、静かに失敗します。アポストロフィを含む名前は二重にしない限り数式を壊し、グラフシートには A1 がないので False が返り、数式はアクティブなブックに対して評価されます。二行を節約する代わりに、バグを三つ抱えることになります。
この確認を土台にするパターン
このチェックの使い道は、ほぼすべてが三つのパターンのどれかで、それぞれに専用のガイドがあります。
' 取得か作成: シートがあれば再利用し、なければ追加する
If SheetExists("Log") Then
Set ws = ThisWorkbook.Worksheets("Log")
Else
Set ws = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
ws.Name = "Log"
End If
' あれば削除: すでになくてもエラー 9 にならない
If SheetExists("Temp") Then
Application.DisplayAlerts = False
ThisWorkbook.Sheets("Temp").Delete
Application.DisplayAlerts = True
End If
一つ目は シートの追加のガイド で、二つ目は最後の表示シートのルールも含めて シートの削除のガイド で詳しく扱っています。三つ目は空いている名前 — Report (2)、Report (3) — の生成で、シート名の変更のガイド が SheetExists の上に組み立てています。
判断の分かれ目:名前は確認するもので、頼るものではない
このチェックが必要なのは、名前が壊れやすいからです。名前を使わ ざるをえない コード — ユーザーが作るシート、セルに入力された名前、外から来るファイル — を安全にしてくれます。自分のシートを名前で探し続ける理由にはなりません。自分のツールが所有するシートには CodeName を使えば、確認するものは何もありません。私のルール:名前が人から来たなら確認する。シートが自分から来たなら、そもそも名前で探さない。 そして閉じているブックについては、信頼に値する近道はありません。読み取り専用 で開き、同じ関数で確認し、閉じます。
ExcelMaster の活用
存在確認のバグは、存在確認のバグには見えません。違うブックに追加されたシート、グラフシートがすでにその名前を持っていたせいで失敗するリネーム、セルに文字列ではなく数値が入っていたせいで一か月分の数字を三番目のシートに書き込んでしまうマクロ、といった姿で現れます。
ExcelMaster には、仕事の内容をそのまま伝えられます — 「A 列の地域ごとに、シートがあれば再利用し、なければ作成して」 — すると、正しいブックと正しいコレクションに対して、名前を文字列として渡すチェックを書いてくれるので、取得か作成のロジックが言葉どおりに動きます。
よくある質問
VBA でシートが存在するか確認するには?
組み込みのメソッドはないので、小さな関数を使います。エラー処理を切り、Set sh = wb.Sheets(name) を試し、エラー処理を元に戻して、Not sh Is Nothing を返します。ワークシートもグラフシートも、表示・非表示を問わず見つけ、Excel と同じく大文字小文字を区別しません。
別のブックにシートが存在するか確認するには?
そのブックを関数に渡します。たとえば SheetExists("Rates", Workbooks("Prices.xlsx")) です。ブックは開いている必要があります。閉じている場合は、読み取り専用で開き、確認して、また閉じます。
シート名の確認は大文字と小文字を区別しますか?
区別しません。Excel は Sales、SALES、sales を同じ名前として扱うので、Sheets(name) による検索は大文字小文字を区別しません。自分でループして名前を比べるなら、StrComp(a, b, vbTextCompare) を使うか LCase$ の値同士を比べれば、同じ結果になります。
チェックはシートがないと言うのに、追加できないのはなぜですか?
たいていは、チェックが Worksheets を検索していて、その名前を Sheets にしか現れないグラフシートが使っているからです。名前は両方を通して一意でなければならないので、Sheets に対して確認しましょう。非表示のシートがその名前を持っている場合もあります。チェックはそれを見つけますが、シート見出しの並びには見えません。
非表示のシートも存在するとみなされますか?
はい。非表示と完全非表示のシートも Sheets コレクションに入っているので、チェックは True を返します。ユーザーに見えているかを知りたいなら、チェックの後で sh.Visible = xlSheetVisible を判定しましょう。
検証環境
検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-09-30。
関連ガイド: VBA シート名の変更 · VBA シートの非表示 · VBA シートの追加 · VBA シートの削除 · VBA Worksheets · VBA On Error · VBA Like · VBA ブックを開く
