Contents
完成イメージ(Before / After)
Before(実行前)
テーブルにデータはあるが、集計は手作業。新しい行の追加もテーブルの最終行を目視で確認してから入力している。
| A(日付) | B(部署) | C(商品名) | D(売上) | |
|---|---|---|---|---|
| 1 | 日付 | 部署 | 商品名 | 売上 |
| 2 | 2026/01/05 | 営業部 | 商品A | 50000 |
| 3 | 2026/01/10 | 総務部 | 商品B | 30000 |
| 4 | 2026/01/15 | 営業部 | 商品C | 80000 |
After(実行後)
VBAでテーブルに新しい行を追加し、「営業部」のデータだけを抽出して売上合計を自動集計。結果は別シートに出力される。
集計結果シート:
| A | B | |
|---|---|---|
| 1 | 集計対象 | 営業部 |
| 2 | テーブル名 | テーブル1 |
| 3 | 全データ件数 | 6件 |
| 4 | 該当件数 | 3件 |
| 5 | 売上合計 | 190,000 |
導入
①共感 — Rangeで最終行を計算するのが面倒
毎月の売上データを集計するマクロで Cells(Rows.Count, 1).End(xlUp).Row を何度も書いていた。Excelではテーブルを使っているのに、VBAになるとRange指定に戻ってしまう。
②実感 — ListObjectで最終行の計算が不要になった
ListObjectを使い始めたら、最終行の計算が不要になった。ListRows.Add で行を追加すれば自動でテーブル範囲が拡張されるし、DataBodyRange で常にデータ全体を取得できる。コードの量が半分になった。
最終行取得の詳しい方法は 最終行を正しく取得する方法 も参照。
③動機 — テーブル操作を知ればVBAの保守性が変わる
Excelのテーブル機能は誰でも使うが、VBAでのテーブル操作は意外と情報が少ない。ListObjectを知っているかどうかで、VBAコードの保守性が大きく変わる。
事前準備
バックアップを取る
既存のデータが入っているExcelファイルで試す場合は、先にファイルをコピーしてバックアップを取ること。
Excelをマクロ有効ブック(.xlsm)で保存する
- Excelを開く(新規でも既存でもOK)
- 「ファイル」→「名前を付けて保存」
- ファイルの種類を 「Excel マクロ有効ブック (*.xlsm)」 に変更して保存
.xlsx のままだとマクロが保存されない。必ず .xlsm にすること。
テーブルを準備する
Sheet1 に以下のデータを入力し、テーブルに変換する。
| A | B | C | D | |
|---|---|---|---|---|
| 1 | 日付 | 部署 | 商品名 | 売上 |
| 2 | 2026/01/05 | 営業部 | 商品A | 50000 |
| 3 | 2026/01/10 | 総務部 | 商品B | 30000 |
| 4 | 2026/01/15 | 営業部 | 商品C | 80000 |
| 5 | 2026/01/20 | 経理部 | 商品D | 45000 |
| 6 | 2026/01/25 | 営業部 | 商品E | 60000 |
テーブルへの変換手順:
- データ範囲内の任意のセル(例:A1)をクリック
- Ctrl + T を押す
- 「先頭行をテーブルの見出しとして使用する」にチェックが入っていることを確認
- 「OK」をクリック
テーブル名を確認するには、テーブル内のセルを選択し、「テーブル デザイン」タブの左上を見る。既定では「テーブル1」になっている。
手順(コピペ → 実行まで約5分)
VBE(コードを書く画面)を開く
Alt + F11 キーを押すとVBE(Visual Basic Editor)が開く。
一般的にはAlt + F11で開けるが、企業のセキュリティ設定でVBAが無効化されている場合は、IT部門に確認すること。
標準モジュールを挿入する
- VBE のメニュー「挿入」→「標準モジュール」をクリック
- 右側にコードウィンドウが表示される → ここにコードを貼り付ける
コードを貼り付ける
下の「コード(最小版)」をコピーして、標準モジュールのコードウィンドウに貼り付ける。
マクロを実行する
- VBEからExcelの画面に戻る(Alt + F11 または タスクバーでExcelをクリック)
- Alt + F8 キーを押す(マクロ一覧が表示される)
- 実行したいマクロ名を選んで「実行」をクリック
コード(最小版)– テーブルのデータ取得と行追加
まずはこれだけで動く。テーブルの基本情報を取得し、新しい行を追加する。
Sub テーブル操作_最小版()
Dim ws As Worksheet
Dim lo As ListObject
Dim newRow As ListRow
' --- 対象シートとテーブルを指定 ---
Set ws = ThisWorkbook.Sheets("Sheet1")
' テーブルが存在するかチェック
If ws.ListObjects.Count = 0 Then
MsgBox "Sheet1にテーブルがありません。" & vbCrLf & _
"データ範囲を選択して Ctrl+T でテーブルを作成してください。", _
vbExclamation
Exit Sub
End If
Set lo = ws.ListObjects(1) ' シート上の最初のテーブル
' --- テーブルの基本情報を表示 ---
Dim info As String
info = "テーブル名: " & lo.Name & vbCrLf
info = info & "データ行数: " & lo.ListRows.Count & "行" & vbCrLf
info = info & "列数: " & lo.ListColumns.Count & "列" & vbCrLf
info = info & "見出し: "
Dim i As Long
For i = 1 To lo.ListColumns.Count
info = info & lo.ListColumns(i).Name
If i < lo.ListColumns.Count Then info = info & ", "
Next i
MsgBox info, vbInformation, "テーブル情報"
' --- 新しい行を追加 ---
Set newRow = lo.ListRows.Add
' 追加した行に値を設定
newRow.Range.Cells(1, 1).Value = Date ' A列: 今日の日付
newRow.Range.Cells(1, 2).Value = "営業部" ' B列: 部署
newRow.Range.Cells(1, 3).Value = "商品F" ' C列: 商品名
newRow.Range.Cells(1, 4).Value = 70000 ' D列: 売上
MsgBox "新しい行を追加しました。", vbInformation
End Sub
コードの動作:
- Sheet1 にテーブルがあるかチェック(なければ警告して終了)
- テーブルの基本情報(名前・行数・列数・見出し名)をメッセージボックスに表示
- テーブルの末尾に新しい行を追加
- 追加した行に日付・部署・商品名・売上を設定
ポイント:
lo.ListRows.Addでテーブルの末尾に行が追加される。最終行の計算は不要newRow.Range.Cells(1, 列番号)で追加した行の各セルにアクセスできる- テーブルが1つもない場合のエラーチェックを入れておくと安全
コード(実務版)– テーブルから条件に合うデータを抽出して集計
業務で使うなら、テーブルのデータを条件で抽出し、集計結果を別シートに出力すると便利。
実行前の準備:
- Sheet1 にテーブルがある(事前準備で作成済み)
- 集計結果シートは自動で作成される
Sub テーブル操作_実務版()
Dim ws As Worksheet
Dim lo As ListObject
Dim lr As ListRow
Dim wsResult As Worksheet
' --- 対象シートとテーブルを指定 ---
Set ws = ThisWorkbook.Sheets("Sheet1")
' テーブルの存在チェック
If ws.ListObjects.Count = 0 Then
MsgBox "Sheet1にテーブルがありません。", vbExclamation
Exit Sub
End If
Set lo = ws.ListObjects(1)
' データが0行の場合のチェック
If lo.DataBodyRange Is Nothing Then
MsgBox "テーブルにデータがありません(見出しのみ)。", vbExclamation
Exit Sub
End If
' --- 集計条件の設定 ---
Dim targetDept As String
targetDept = "営業部" ' ← ここを変更すれば別の部署を集計できる
' --- 列番号を列名から取得(列の順番が変わっても対応) ---
Dim colDept As Long
Dim colSales As Long
On Error Resume Next
colDept = lo.ListColumns("部署").Index
colSales = lo.ListColumns("売上").Index
On Error GoTo 0
If colDept = 0 Or colSales = 0 Then
MsgBox "テーブルに「部署」または「売上」列が見つかりません。" & vbCrLf & _
"見出し名を確認してください。", vbExclamation
Exit Sub
End If
' --- エラーハンドラを設定 ---
On Error GoTo ErrHandler
' --- データをループして条件に合う行を集計 ---
Dim totalSales As Double
Dim matchCount As Long
Dim salesValue As Variant
totalSales = 0
matchCount = 0
For Each lr In lo.ListRows
If lr.Range.Cells(1, colDept).Value = targetDept Then
salesValue = lr.Range.Cells(1, colSales).Value
' 数値チェック(文字列が混在している場合に備える)
If IsNumeric(salesValue) Then
totalSales = totalSales + CDbl(salesValue)
End If
matchCount = matchCount + 1
End If
Next lr
' --- 集計結果を別シートに出力 ---
' 「集計結果」シートがなければ作成
On Error Resume Next
Set wsResult = ThisWorkbook.Sheets("集計結果")
On Error GoTo ErrHandler
If wsResult Is Nothing Then
Set wsResult = ThisWorkbook.Sheets.Add(After:=ws)
wsResult.Name = "集計結果"
Else
wsResult.Cells.ClearContents
End If
' 結果を書き込む
wsResult.Range("A1").Value = "集計対象"
wsResult.Range("B1").Value = targetDept
wsResult.Range("A2").Value = "テーブル名"
wsResult.Range("B2").Value = lo.Name
wsResult.Range("A3").Value = "全データ件数"
wsResult.Range("B3").Value = lo.ListRows.Count & "件"
wsResult.Range("A4").Value = "該当件数"
wsResult.Range("B4").Value = matchCount & "件"
wsResult.Range("A5").Value = "売上合計"
wsResult.Range("B5").Value = totalSales
wsResult.Range("B5").NumberFormat = "#,##0"
' 見出し列を太字にする
wsResult.Range("A1:A5").Font.Bold = True
wsResult.Columns("A:B").AutoFit
wsResult.Activate
MsgBox targetDept & "の集計が完了しました。" & vbCrLf & _
"件数: " & matchCount & "件" & vbCrLf & _
"売上合計: " & Format(totalSales, "#,##0") & "円", _
vbInformation, "集計完了"
Exit Sub
ErrHandler:
MsgBox "エラーが発生しました。" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
"内容: " & Err.Description, vbCritical, "エラー"
End Sub
追加ポイント:
- テーブルの存在チェック、データ0行チェック、列名チェックの3段構えでエラーを防止。エラー処理の基本は エラー処理で安全なマクロにする方法 も参照
ListColumns("列名").Indexで列番号を動的に取得するので、列の順番が変わっても動く- 売上列の値が数値かどうかを
IsNumericでチェック。文字列が混在していても安全 - 集計結果を「集計結果」シートに自動出力。シートがなければ新規作成
targetDeptの値を変更すれば、別の部署の集計にすぐ使える- 抽出処理の応用は データの抽出方法 も参照
ListObject の主要プロパティ一覧
| プロパティ / メソッド | 説明 | 使用例 |
|---|---|---|
ListObjects(1) |
シート上の最初のテーブルを取得 | Set lo = ws.ListObjects(1) |
ListObjects("名前") |
テーブルを名前で取得 | Set lo = ws.ListObjects("テーブル1") |
.Name |
テーブル名の取得・設定 | lo.Name = "売上テーブル" |
.ListRows |
データ行のコレクション | lo.ListRows.Count で行数 |
.ListColumns |
列のコレクション | lo.ListColumns("売上").Index |
.HeaderRowRange |
見出し行の Range | lo.HeaderRowRange.Value |
.DataBodyRange |
データ部分の Range(0行なら Nothing) | lo.DataBodyRange.Copy |
.Range |
テーブル全体の Range(見出し+データ) | lo.Range.Select |
.ListRows.Add |
新しい行を末尾に追加 | Set nr = lo.ListRows.Add |
.ListObjects.Add |
新しいテーブルを作成 | 下記コード参照 |
.Unlist |
テーブルを通常の範囲に戻す | lo.Unlist |
テーブルの作成と解除のコード例
' --- テーブルの作成 ---
Sub テーブルを作成する()
Dim ws As Worksheet
Dim lo As ListObject
Set ws = ThisWorkbook.Sheets("Sheet1")
Set lo = ws.ListObjects.Add( _
SourceType:=xlSrcRange, _
Source:=ws.Range("A1:D10"), _
XlListObjectHasHeaders:=xlYes)
lo.Name = "売上テーブル"
MsgBox "テーブル「" & lo.Name & "」を作成しました。", vbInformation
End Sub
' --- テーブルの解除 ---
Sub テーブルを解除する()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
If ws.ListObjects.Count = 0 Then
MsgBox "テーブルがありません。", vbExclamation
Exit Sub
End If
ws.ListObjects(1).Unlist
MsgBox "テーブルを解除しました(データと書式は残ります)。", vbInformation
End Sub
注意: Unlist でテーブルを解除すると、テーブルの自動拡張・構造化参照・オートフィルタ連動などの機能が失われる。解除前にバックアップを取ること。
Range指定との比較 — なぜ ListObject を使うのか
従来のRange指定と ListObject を比較すると、テーブルを使うメリットがわかる。
※以下は別々のプロシージャに書く前提の比較例です。
' === Range指定の場合 ===
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim rngData As Range
Set rngData = ws.Range("A2:D" & lastRow)
' 行を追加
ws.Cells(lastRow + 1, 1).Value = Date
ws.Cells(lastRow + 1, 2).Value = "営業部"
' === ListObject の場合 ===
Dim lo As ListObject
Set lo = ws.ListObjects("テーブル1")
Dim tblData As Range
Set tblData = lo.DataBodyRange
' 行を追加
Dim newRow As ListRow
Set newRow = lo.ListRows.Add
newRow.Range.Cells(1, 1).Value = Date
newRow.Range.Cells(1, 2).Value = "営業部"
ListObject を使えば最終行の計算が不要になり、列の追加・削除にも強いコードが書ける。最終行取得で毎回 End(xlUp).Row を書いていた人は 最終行を正しく取得する方法 とあわせて読むとよい。
よくある落とし穴5選
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| 1 | 「実行時エラー ‘9’: インデックスが有効範囲にありません」 | VBAコード内のテーブル名と実際のテーブル名が一致していない。日本語環境の既定名は「テーブル1」だが、手動で変更していると異なる | テーブル内のセルを選択 →「テーブル デザイン」タブの左上でテーブル名を確認し、コード内の名前を合わせる |
| 2 | 「実行時エラー ’91’: オブジェクト変数が設定されていません」 | テーブルにデータが1行もない(見出しのみ)状態で DataBodyRange を参照した |
If lo.DataBodyRange Is Nothing Then で事前にチェックしてから処理する |
| 3 | ListRows.Add で行を追加したが値の入れ方がわからない | 追加した行への値の設定方法が直感的ではない | newRow.Range.Cells(1, 列番号).Value = 値 の形で設定する。列番号は左から1,2,3… |
| 4 | Unlist(テーブル解除)したらオートフィルタが消えた | Unlist はテーブルの構造情報をすべて削除する。オートフィルタ・自動拡張・構造化参照が失われる | 解除前にバックアップを取る。オートフィルタだけ使いたい場合は オートフィルタの操作方法 を参照 |
| 5 | 列名で指定したのに「インデックスが有効範囲にありません」 | テーブルの見出し名が変更された、または見出しに不要な空白や改行が含まれている | Debug.Print lo.ListColumns(i).Name で実際の列名をイミディエイトウィンドウに出力して確認する |
FAQ
Q1: テーブル名はどこで確認できる?
テーブル内のセルをクリックすると「テーブル デザイン」タブが表示される。そのタブの左上に「テーブル名」が表示されている。VBAでは Debug.Print ActiveSheet.ListObjects(1).Name でイミディエイトウィンドウに出力して確認することもできる。
Q2: テーブルが複数あるシートではどう指定する?
ws.ListObjects("テーブル名") で名前を指定するのが確実。インデックス(1, 2, 3…)でも指定できるが、テーブルの追加・削除で順番が変わる可能性があるため、名前指定を推奨。
Q3: 既存の Range 指定のコードをテーブル対応に書き換えるには?
主な置き換えは3点。(1) Cells(Rows.Count,1).End(xlUp).Row → lo.ListRows.Count (2) Range("A2:D" & lastRow) → lo.DataBodyRange (3) 行追加 → lo.ListRows.Add。1つずつ動作確認しながら書き換えるのが安全。
Q4: テーブルの見出し(列名)を変更したらコードは動かなくなる?
ListColumns("売上") のように列名で指定している場合は動かなくなる。ListColumns(4) のようにインデックスで指定すれば影響しないが、コードの可読性は下がる。運用上、列名を変更する可能性がある場合はインデックス指定を検討する。
Q5: テーブルを作らずに ListObject だけ使える?
使えない。ListObject は Excelのテーブル機能と紐づいたオブジェクトなので、セル範囲をテーブルに変換(Ctrl+T または ListObjects.Add)してから使う必要がある。
まとめ
この記事で、VBAでExcelのテーブル(ListObject)を操作できるようになった。
- 最小版:テーブルの基本情報を取得し、新しい行を追加
- 実務版:テーブルから条件に合うデータを抽出して集計し、別シートに出力
ListObject を使うと、Range指定で毎回最終行を計算する必要がなくなり、コードがシンプルになる。テーブルの列名で参照できるため、列の追加・並び替えにも強い。
関連記事
- 【VBA】データの抽出方法(コピペOK) — テーブル以外のデータ抽出方法
- 【VBA】最終行を正しく取得する方法(コピペOK) — Range指定での最終行取得
- 【VBA】オートフィルタの操作方法(コピペOK) — テーブルのフィルタ操作
- 【VBA】エラー処理で安全なマクロにする方法(コピペOK) — テーブル存在チェック等のエラー処理
- 【VBA】セルの書式を変更する方法(コピペOK) — テーブルの書式関連
次にやりたくなること
- 【VBA】オートフィルタをVBAで操作する方法 — テーブルのフィルタを VBA で自動制御したい場合
- 【VBA】条件に合うデータを抽出する方法 — テーブル以外のデータでも条件抽出を自動化したい場合
もっとカスタマイズしたい場合
「複数テーブルを横断して集計したい」「テーブルのデータを別のExcelファイルに転記したい」「テーブルの構造ごとバックアップを取りたい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できます。
相談時に以下の情報があるとスムーズです:
- Excel のバージョン / OS
- テーブルの数とレイアウト(列名)
- どのような集計・加工をしたいか
- 出力先(同じシート / 別シート / 別ファイル)


コメント