この記事で分かること
- AIにVBAを書かせるときに、最初に伝えるべき前提情報
- 「動かないコードが返ってくる」を減らすプロンプトの組み立て方
- 返ってきたコードをそのまま実行する前に確認すべきチェックリスト
「AIにマクロを頼んでも、自分のファイルに合わないコードが返ってきて結局書き直している」という方に向けて書いています。
どんな場面で使うか
- VBAをこれから学ぶ・まだ慣れていない状態で、定型処理を自動化したい
- 忙しくて自分でコードを書く時間がないので、AIに下書きを作ってもらいたい
- AIが書いたコードを、業務で配布する前に安全性を確認したい
- 過去にAIの回答をそのまま実行してエラーになった経験がある
基本説明
ChatGPTやClaudeは「聞かれたことには答えるが、聞かれていない前提は補ってくれない」という性質があります。
VBAの場合、次の情報が本文中に書かれていなければAIは知りようがありません。
- シートの構成(シート名、何行目からデータがあるか)
- 列の意味(A列は日付、B列は担当者、など)
- 使っているExcelのバージョン(新しい関数・機能が使えるか)
- 処理の実行タイミング(ボタンを押したとき、ファイルを開いたとき、など)
「売上データを集計するマクロを書いて」だけでは、AIは架空のシート構成を想像して書くしかありません。結果、自分のファイルには合わないコードが返ってきます。
手順
ステップ1:前提情報を箇条書きでまとめる
コードを頼む前に、次の4点を箇条書きにしておきます。
- シート名と、データが何行目から始まるか
- 各列の見出しと意味
- やりたい処理を1文で(「〇〇のとき、〇〇をする」の形)
- 使っているExcelのバージョンとOS(Microsoft 365 / Windows など)
4番目にOSを入れているのは、VBAにはWindowsでしか動かない機能があるためです(後述のScripting.Dictionaryがその一つ)。伝えなければAIはWindows前提で書くので、Mac環境や、配布先にMacが混ざる職場では動かないコードが返ってきます。
ステップ2:悪いプロンプトと良いプロンプトを比較する
悪い例(前提情報なし)
売上データを集計するマクロを書いて
良い例(前提情報あり)
Windows版Excel(Microsoft 365)のVBAマクロを書いてください。
- シート名は「売上データ」
- A列: 日付、B列: 担当者、C列: エリア、D列: 商品名、E列: 数量、F列: 売上金額
- データは2行目から最終行まで入っている(行数は毎回変わる)
- やりたいこと:担当者ごとの売上金額の合計を、「集計」シートのA列・B列に出力する
- Option Explicit を使い、Select や Activate は使わないでください
- 前提が足りない場合は、コードを書く前に質問してください
良い例では「Option Explicitを使う」「Select/Activateを使わない」というコーディング方針まで指定している点もポイントです。指定しないと、AIはマクロ記録に近い書き方(Select/Activateを多用するコード)を返すことがあります。
ステップ3:返ってきたコードを検収する
コードが返ってきたら、実行する前に次の「よくあるエラー」セクションのチェックリストで確認します。
サンプルコードまたは例
入力データ(「売上データ」シート)
プロンプトで説明した構成を、実際の表にすると次のようになります。1行目が見出しで、データは2行目から始まります。
| 行 | A:日付 | B:担当者 | C:エリア | D:商品名 | E:数量 | F:売上金額 |
|---|---|---|---|---|---|---|
| 1 | 日付 | 担当者 | エリア | 商品名 | 数量 | 売上金額 |
| 2 | 2026/8/3 | 佐藤 | 東日本 | A-100 | 10 | 120000 |
| 3 | 2026/8/3 | 鈴木 | 西日本 | B-200 | 5 | 75000 |
| 4 | 2026/8/4 | 佐藤 | 東日本 | B-200 | 8 | 120000 |
| 5 | 2026/8/4 | 高橋 | 東日本 | A-100 | 12 | 144000 |
| 6 | 2026/8/5 | 鈴木 | 西日本 | C-300 | 3 | 36000 |
| 7 | 2026/8/5 | 佐藤 | 東日本 | C-300 | 6 | 72000 |
| 8 | 2026/8/6 | 高橋 | 東日本 | B-200 | 4 | 60000 |
「データは2行目から最終行まで入っている(行数は毎回変わる)」と伝えているので、8行目までという情報はAIに渡していません。行数が変わっても動くコードを書いてもらうためです。
出力イメージ(「集計」シート)
上のデータに対して期待する結果です。依頼する前に、この形を自分で書き出しておくと、プロンプトの「やりたいこと」がぶれません。
| 行 | A:担当者 | B:売上金額合計 |
|---|---|---|
| 1 | 担当者 | 売上金額合計 |
| 2 | 佐藤 | 312000 |
| 3 | 鈴木 | 111000 |
| 4 | 高橋 | 204000 |
佐藤さんは 120000 + 120000 + 72000 で 312000、鈴木さんは 75000 + 36000 で 111000、高橋さんは 144000 + 60000 で 204000 です。合計は元データの売上金額の総和 627000 と一致します。
並び順は五十音順ではなく、元データに最初に出てきた順(佐藤 → 鈴木 → 高橋)になります。並び順にこだわりがある場合は、それもプロンプトに書いておく必要があります。
返ってきたコードの例
上記の良いプロンプトに対して返ってくることが期待できるコードの一例です(実際の出力はAIやバージョンにより変わります)。
Option Explicit
Sub 担当者別集計()
Dim wsData As Worksheet
Dim wsSummary As Worksheet
Dim lastRow As Long
Dim i As Long
Dim dict As Object
Dim key As Variant
Dim outputRow As Long
Set wsData = ThisWorkbook.Worksheets("売上データ")
Set wsSummary = ThisWorkbook.Worksheets("集計")
Set dict = CreateObject("Scripting.Dictionary")
lastRow = wsData.Cells(wsData.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastRow
key = wsData.Cells(i, "B").Value
If dict.Exists(key) Then
dict(key) = dict(key) + wsData.Cells(i, "F").Value
Else
dict.Add key, wsData.Cells(i, "F").Value
End If
Next i
wsSummary.Range("A:B").ClearContents
wsSummary.Range("A1").Value = "担当者"
wsSummary.Range("B1").Value = "売上金額合計"
outputRow = 2
For Each key In dict.Keys
wsSummary.Cells(outputRow, "A").Value = key
wsSummary.Cells(outputRow, "B").Value = dict(key)
outputRow = outputRow + 1
Next key
End Sub
コードが何をしているか
AIが返してきたコードは、検収する前に何をしているか読めている必要があります。読めないコードは、動いているのか間違っているのか判断できないためです。上のコードは4つのかたまりに分かれています。
① 使うシートと「集計箱」を用意する
Set wsData = ThisWorkbook.Worksheets("売上データ")
Set wsSummary = ThisWorkbook.Worksheets("集計")
Set dict = CreateObject("Scripting.Dictionary")
wsData・wsSummaryは、シートにつけたあだ名だと考えてください。以降はwsDataと書くだけで「売上データ」シートを指せます。毎回シート名を書かずに済み、シート名を変えたいときも1行直すだけで済みます。
Dictionaryは、名前のラベルが付いた集計箱です。「佐藤」というラベルの箱に金額を入れておき、また佐藤さんが出てきたら同じ箱に足す、という使い方をします。担当者が何人いるか事前に分からなくても、出てきた分だけ箱が増えていきます。
一言でいうと、担当者が何人いるか事前に分からない集計表を、コードの中に作るための入れ物です。シート上に作業列を用意しなくても、「キー(担当者)→ 合計値」の対応表をメモリの中だけで持ち回れます。
Mac版Excelでは
Scripting.Dictionaryは使えません。 これはWindowsのスクリプティング機能に依存しているためで、Mac環境で実行すると「実行時エラー429」などで止まります。配布先にMacが混ざる職場では、Dictionaryを使わない書き方(配列+Match関数など)を指定して頼み直す必要があります。
② データが何行目まであるか調べる
lastRow = wsData.Cells(wsData.Rows.Count, "A").End(xlUp).Row
Rows.Countはシートの最終行(1048576行目)です。そこからEnd(xlUp)で上に向かって、値が入っているセルに当たるまでジャンプします。手作業でいうと、A列の一番下でCtrl + ↑を押した動きと同じです。
サンプルデータでは8行目に当たるので、lastRowは8になります。行数が変わっても毎回この計算をやり直すので、データが増減しても直す必要がありません。
③ 1行ずつ読んで、担当者ごとに足していく
For i = 2 To lastRow
key = wsData.Cells(i, "B").Value
If dict.Exists(key) Then
dict(key) = dict(key) + wsData.Cells(i, "F").Value
Else
dict.Add key, wsData.Cells(i, "F").Value
End If
Next i
ここが処理の中心です。iが2から8まで1つずつ増えていき、Cells(i, "B")は「i行目のB列」=担当者を指します。同じくCells(i, "F")が売上金額です。
やっていることは次の2択だけです。
- その担当者の箱がすでにある(
dict.Exists(key)が成立)→ 箱の中身に今回の金額を足す - まだ無い(
Else)→ 今回の金額を入れた箱を新しく作る
サンプルデータで実際に追いかけると、箱の中身はこう変化します。
| 読む行 | 担当者 | 売上金額 | 箱があるか | 処理後の箱の中身 |
|---|---|---|---|---|
| 2 | 佐藤 | 120000 | 無い→作る | 佐藤 120000 |
| 3 | 鈴木 | 75000 | 無い→作る | 佐藤 120000 / 鈴木 75000 |
| 4 | 佐藤 | 120000 | ある→足す | 佐藤 240000 / 鈴木 75000 |
| 5 | 高橋 | 144000 | 無い→作る | 佐藤 240000 / 鈴木 75000 / 高橋 144000 |
| 6 | 鈴木 | 36000 | ある→足す | 佐藤 240000 / 鈴木 111000 / 高橋 144000 |
| 7 | 佐藤 | 72000 | ある→足す | 佐藤 312000 / 鈴木 111000 / 高橋 144000 |
| 8 | 高橋 | 60000 | ある→足す | 佐藤 312000 / 鈴木 111000 / 高橋 204000 |
8行目を読み終えた時点で、出力イメージの表と同じ3件が箱に揃っています。箱が作られた順番が佐藤 → 鈴木 → 高橋なので、出力の並び順もこの順になります。先ほど「元データに最初に出てきた順」と書いたのはこのためです。
AIは同じ処理を、もっと短い形で返してくることもあります。
For i = 2 To lastRow
key = wsData.Cells(i, "B").Value
dict(key) = dict(key) + wsData.Cells(i, "F").Value
Next i
Ifが消えて1行になっていますが、結果は同じです。Dictionaryはまだ無いキーを読もうとすると、その場で空の箱を勝手に作るという性質があり、空の箱の中身は数値の0として扱われるため、いきなり足し算をしても成立します。
どちらが返ってきても間違いではありません。ただし短い形は「新規なら作る」という判断が読み取れないので、慣れないうちはIf dict.Exists(key)が書かれているほうを選んだほうが、後から読み返したときに困りません。「なぜ動くのか分からないコード」は、検収できないという意味で危険です。
④ 箱の中身を集計シートに書き出す
outputRow = 2
For Each key In dict.Keys
wsSummary.Cells(outputRow, "A").Value = key
wsSummary.Cells(outputRow, "B").Value = dict(key)
outputRow = outputRow + 1
Next key
dict.Keysは箱のラベル一覧(佐藤・鈴木・高橋)です。For Eachでそれを1つずつ取り出し、A列にラベル、B列に中身の金額を書きます。
outputRowは書き込む行の位置で、2から始めて1件書くたびに1ずつ増やします。見出しを1行目に書いているので、データは2行目からになります。この変数がないと、3件とも同じ行に上書きされてしまいます。
なお、この処理は担当者が2〜3人ならピボットテーブルやSUMIF関数でも同じ結果が出せます。VBAが効いてくるのは、毎月・毎週この集計を繰り返す場合や、集計のあとに続けて別の処理(ファイル保存やメール送信など)をつなげたい場合です。
検収の観点で見ると
Dictionaryを使って担当者ごとに合計を集めており、SelectやActivateは使われていません。最終行をEnd(xlUp)で毎回取得しているため、行数が変わっても動作します。書き込みの前にRange("A:B").ClearContentsを入れているのは、前回より担当者が減ったときに古い行が残らないようにするためです。
一方で、このコードにはシートが見つからないときのエラー処理が入っていません。「売上データ」または「集計」というシートが無いブックで実行すると、その場でエラーが出て止まります。後述するチェックリストの3番目に引っかかる状態なので、業務で配布するなら「シートが存在しない場合はメッセージを出して終了するようにして」と追加で頼みます。AIは頼んでいないことはやらない、という性質がそのまま出ている例です。
よくあるエラー
検収チェックリスト
返ってきたコードを実行する前に、次を確認します。
Option Explicitがあるか:無ければ先頭に追加する(変数のタイプミスに気づけなくなるため)。Select・Activateを使っていないか:使われている場合、画面のアクティブシートに依存する不安定なコードになっている可能性が高い。「Range/Cellsで直接指定するように書き直して」と頼み直す(違いはSelect、Activate、他セル指定方法の比較で詳しく扱っています)。- エラー処理があるか:シートが見つからない、値が空、といった想定外の入力にどう対応するか確認する。何も書かれていなければ「シートが存在しない場合はメッセージを出して終了するようにして」と追加依頼する。
- ループの終了条件が固定値になっていないか:
For i = 2 To 100のように行数が決め打ちになっていると、データが増えたときに動かなくなる。End(xlUp)などで最終行を取得しているか確認する(コピペで使える定番スニペット10選に最終行取得の定型コードをまとめています)。 - 書き込み先が意図した範囲か:集計結果を上書きしてよいセルかどうか、元データを壊す処理になっていないかを確認する。
- 集計キーの表記ゆれが考慮されているか:
Dictionaryは「佐藤」と「佐藤 」(末尾に空白)を別人として集計します。全角と半角、文字列の"1"と数値の1も別扱いです。手入力された列を集計キーにする場合はほぼ必ず起きるので、「担当者名は前後の空白を除いてから集計して」(Trim)とプロンプトに書き足す。 - 自分の環境で動く書き方か:
Scripting.DictionaryのようにWindows限定の機能が使われていないか確認する。配布先にMacが混ざるなら、依頼の時点でOSを伝えておく。
これらを満たさないコードを見つけたら、「〇〇を直して」と部分的に頼み直すほうが、最初から全部を書き直させるより早く収束します。
まとめ
AIにVBAを頼むときは、シート構成・列の意味・Excelのバージョンと使っているOSといった前提情報を先に伝えることが、動くコードを得る一番の近道です。さらに「Option Explicitを使う」「Select/Activateを使わない」といったコーディング方針まで指定すると、検収の手間が大きく減ります。返ってきたコードは、この記事のチェックリストで確認してから実行するようにしてください。
関連記事
- 【Excel VBA】Select、Activate、他セル指定方法の比較 — 検収チェックリストで「使わない」とした書き方が、なぜ不安定になるのか
- 【VBA】コピペで使える定番スニペット10選(最終行取得・シート存在確認・ファイル選択ほか) — AIに頼むまでもない定型処理は、こちらのコードをそのまま使えます
- 【VBA】実行時エラー番号別の原因と対処一覧(1004・9・13・91…) — 返ってきたコードがエラーで止まったときの調べ方
- 【Excel VBA】マクロの始め方 — そもそもコードをどこに貼り付けて実行するのか
