Excelで表を作るとき、Range("A1")で地道にセルを操作していませんか?実は、テーブル(ListObject)を使えば、見出し行を自動スキップして最終行まで安全にループできるなど、もっとスマートなVBAが書けます。今回は、テーブルをVBAで”完全操作”するための核心オブジェクト「ListObject」を、追加・削除・列名参照・合計行まで丁寧に解説します。
この記事で学べること:
- テーブルとは何か・なぜVBAで使うと強いのか
- テーブルの各部位(Range / DataBodyRange / HeaderRowRange / TotalsRowRange)の違い
- テーブルをVBAで作成する方法(ListObjects.Add)
- データ行の追加・削除(ListRows.Add / Delete)
- 列名で列を参照する方法(ListColumns(“列名”))
- 合計行の表示・集計方法(ShowTotals / TotalsCalculation)
- 実務で使える「まとめサンプルコード」
- よくあるエラーと対処法
- まとめ
テーブルとは?VBAで使う理由
テーブルとは「挿入」→「テーブル」で作成できるExcelの機能です。
Rangeとの違いを表で比較:
| 比較項目 | 通常のRange | テーブル(ListObject) |
|---|---|---|
| 最終行取得 | End(xlUp)で毎回取得が必要 | DataBodyRangeで自動 |
| 見出し行の扱い | 手動でスキップ(i=2〜) | 自動的に除外される |
| 列の参照 | 列番号で指定(壊れやすい) | 列名で指定(列を追加しても壊れない) |
| データ追加 | 範囲の拡張が必要 | ListRows.Addで自動拡張 |
「Range("A1:D100")のような固定範囲指定は、データが増減するたびに修正が必要です。テーブルを使えば、こうした煩わしさから解放されます!」
テーブルの構造を理解する(最重要)
.Range
テーブル全体(見出し行+データ行+集計行すべてを含む)
.DataBodyRange
データ部分のみ(見出し行・集計行を除く)← ループ処理はここを使います!
.HeaderRowRange
見出し(ヘッダー)行のみ
.TotalsRowRange
集計行のみ(ShowTotals=True のときのみ存在)
構造を説明する表:
| プロパティ | 含む範囲 | 主な用途 |
|---|---|---|
| .Range | 全体(見出し+データ+集計) | テーブル全体を選択・書式設定 |
| .DataBodyRange | データのみ | ループ処理・値のクリア |
| .HeaderRowRange | 見出しのみ | 見出し名の取得・変更 |
| .TotalsRowRange | 集計行のみ | 集計行の値取得・書式設定 |
テーブルをVBAで取得する
Sub テーブルを取得する()
Dim ws As Worksheet
Dim tbl As ListObject
Set ws = ActiveSheet
' 方法1:テーブル名で取得(おすすめ)
Set tbl = ws.ListObjects("売上テーブル")
' 方法2:インデックスで取得
' Set tbl = ws.ListObjects(1)
' 方法3:セルから取得
' Set tbl = ws.Range("A1").ListObject
MsgBox "テーブル名:" & tbl.Name & vbCrLf & _
"行数:" & tbl.ListRows.Count & vbCrLf & _
"列数:" & tbl.ListColumns.Count
End Sub
「テーブル名での取得が最も安全です。名前はExcelの[テーブルデザイン]タブで確認・変更できます。」
テーブルをVBAで作成する(ListObjects.Add)
Sub テーブルを作成する()
Dim ws As Worksheet
Dim tbl As ListObject
Set ws = ActiveSheet
' すでにテーブルが存在するかチェック
If ws.ListObjects.Count > 0 Then
MsgBox "すでにテーブルが存在します"
Exit Sub
End If
' A1セルを起点に自動でデータ範囲を検出してテーブル化
Set tbl = ws.ListObjects.Add( _
SourceType:=xlSrcRange, _
Source:=ws.Range("A1").CurrentRegion, _
XlListObjectHasHeaders:=xlYes)
' テーブル名を設定
tbl.Name = "売上テーブル"
' スタイルを設定(青系のスタイル)
tbl.TableStyle = "TableStyleMedium2"
MsgBox "テーブルを作成しました!"
End Sub
「CurrentRegionを使うと、データが入っている範囲を自動検出してくれます。」
データ行を追加する(ListRows.Add)【よく使う!】
Sub データ行を追加する()
Dim ws As Worksheet
Dim tbl As ListObject
Dim newRow As ListRow
Set ws = ActiveSheet
Set tbl = ws.ListObjects("売上テーブル")
' テーブルの末尾に新しい行を追加
Set newRow = tbl.ListRows.Add
' 列名で値を設定(列番号が変わっても壊れない!)
newRow.Range(tbl.ListColumns("日付").Index) = Date
newRow.Range(tbl.ListColumns("商品名").Index) = "ノートPC"
newRow.Range(tbl.ListColumns("数量").Index) = 3
newRow.Range(tbl.ListColumns("単価").Index) = 80000
MsgBox "データを追加しました!"
End Sub
「ListColumns("列名").Indexで列番号を取得するのがポイント。列の順番が変わっても壊れないコードになります。」
特定の行を削除する(ListRows.Delete)
Sub 条件に合う行を削除する()
Dim ws As Worksheet
Dim tbl As ListObject
Dim i As Long
Set ws = ActiveSheet
Set tbl = ws.ListObjects("売上テーブル")
' 削除はループを「下から」行うのが鉄則!
For i = tbl.ListRows.Count To 1 Step -1
' 「数量」列が0の行を削除
If tbl.ListRows(i).Range(tbl.ListColumns("数量").Index) = 0 Then
tbl.ListRows(i).Delete
End If
Next i
MsgBox "削除が完了しました!"
End Sub
「⚠️ 削除ループは必ず「下から(Step -1)」回してください。上から削除するとインデックスがずれてスキップが発生します。」
列名参照でデータ全体を処理する(DataBodyRange活用)
Sub 列名参照でループ処理する()
Dim ws As Worksheet
Dim tbl As ListObject
Dim row As ListRow
Dim 売上列 As Long
Dim 判定列 As Long
Set ws = ActiveSheet
Set tbl = ws.ListObjects("売上テーブル")
' 列インデックスを変数に格納(可読性UP)
売上列 = tbl.ListColumns("売上金額").Index
判定列 = tbl.ListColumns("判定").Index
' 全データ行をループ(見出しは自動スキップ!)
For Each row In tbl.ListRows
If row.Range(売上列) >= 100000 Then
row.Range(判定列) = "優良"
ElseIf row.Range(売上列) >= 50000 Then
row.Range(判定列) = "通常"
Else
row.Range(判定列) = "要確認"
End If
Next row
MsgBox "判定処理が完了しました!"
End Sub
「For Each row In tbl.ListRowsで全データ行をループできます。見出し行のスキップが不要なのでコードが一気にシンプルになります!」
合計行を表示・設定する(ShowTotals / TotalsCalculation)
Sub 合計行を設定する()
Dim ws As Worksheet
Dim tbl As ListObject
Set ws = ActiveSheet
Set tbl = ws.ListObjects("売上テーブル")
' 合計行を表示
tbl.ShowTotals = True
' 「数量」列→合計(SUM)
tbl.ListColumns("数量").TotalsCalculation = xlTotalsCalculationSum
' 「売上金額」列→合計(SUM)
tbl.ListColumns("売上金額").TotalsCalculation = xlTotalsCalculationSum
' 「商品名」列→件数(COUNT)
tbl.ListColumns("商品名").TotalsCalculation = xlTotalsCalculationCount
MsgBox "合計行を設定しました!"
End Sub
TotalsCalculationの設定値一覧表:
| 定数 | 集計方法 |
|---|---|
| xlTotalsCalculationNone | なし(空白) |
| xlTotalsCalculationSum | 合計 |
| xlTotalsCalculationAverage | 平均 |
| xlTotalsCalculationCount | データの個数 |
| xlTotalsCalculationMax | 最大値 |
| xlTotalsCalculationMin | 最小値 |
まとめサンプル:売上テーブルを一括管理するマクロ
(以下の処理をすべて行う実務的なサンプルコード)
- テーブルが無ければ作成する
- 新しいデータ行を1件追加する
- 「数量」が0の行を削除する
- 売上金額に応じて「判定」列を自動入力する
- 合計行を表示して合計・件数を設定する
Sub 売上テーブルを一括管理する()
Dim ws As Worksheet
Dim tbl As ListObject
Dim newRow As ListRow
Dim row As ListRow
Dim i As Long
Set ws = ActiveSheet
'-------------------------------------------
' STEP1: テーブルの存在確認・作成
'-------------------------------------------
If ws.ListObjects.Count = 0 Then
Set tbl = ws.ListObjects.Add( _
SourceType:=xlSrcRange, _
Source:=ws.Range("A1").CurrentRegion, _
XlListObjectHasHeaders:=xlYes)
tbl.Name = "売上テーブル"
tbl.TableStyle = "TableStyleMedium2"
Else
Set tbl = ws.ListObjects(1)
End If
'-------------------------------------------
' STEP2: 新しいデータ行を追加
'-------------------------------------------
Set newRow = tbl.ListRows.Add
newRow.Range(tbl.ListColumns("日付").Index) = Date
newRow.Range(tbl.ListColumns("商品名").Index) = "サンプル商品"
newRow.Range(tbl.ListColumns("数量").Index) = 5
newRow.Range(tbl.ListColumns("単価").Index) = 12000
newRow.Range(tbl.ListColumns("売上金額").Index) = 5 * 12000
'-------------------------------------------
' STEP3: 数量0の行を削除(下からループ)
'-------------------------------------------
For i = tbl.ListRows.Count To 1 Step -1
If tbl.ListRows(i).Range(tbl.ListColumns("数量").Index) = 0 Then
tbl.ListRows(i).Delete
End If
Next i
'-------------------------------------------
' STEP4: 売上金額に応じて判定を自動入力
'-------------------------------------------
Dim 売上列 As Long: 売上列 = tbl.ListColumns("売上金額").Index
Dim 判定列 As Long: 判定列 = tbl.ListColumns("判定").Index
For Each row In tbl.ListRows
Select Case True
Case row.Range(売上列) >= 100000
row.Range(判定列) = "優良"
Case row.Range(売上列) >= 50000
row.Range(判定列) = "通常"
Case Else
row.Range(判定列) = "要確認"
End Select
Next row
'-------------------------------------------
' STEP5: 合計行の設定
'-------------------------------------------
tbl.ShowTotals = True
tbl.ListColumns("数量").TotalsCalculation = xlTotalsCalculationSum
tbl.ListColumns("売上金額").TotalsCalculation = xlTotalsCalculationSum
tbl.ListColumns("商品名").TotalsCalculation = xlTotalsCalculationCount
MsgBox "売上テーブルの管理処理が完了しました!" & vbCrLf & _
"データ行数:" & tbl.ListRows.Count & "件"
End Sub
よくあるエラーと対処法
| エラー内容 | 原因 | 対処法 |
|---|---|---|
| 実行時エラー9 「インデックスが有効範囲にありません」 | テーブル名の指定ミス | ListObjects.Countで確認し、正しいテーブル名を使う |
| 実行時エラー1004 | テーブル外の操作やテーブルが存在しない | テーブルの存在チェック後に操作する |
| 削除時に行がスキップされる | 上からループで削除している | Step -1(下からループ)に変更する |
| TotalsRowRangeがNullになる | ShowTotals=Falseの状態でアクセス | ShowTotals=Trueにしてからアクセスする |
| 列が見つからない (ListColumns(“列名”)でエラー) | 列名のタイポ or 存在しない列 | HeaderRowRangeで実際の列名を確認する |
まとめ
この記事で学んだことの整理:
- ListObjectはテーブルを表すオブジェクト。名前や列名で安全に操作できる
- DataBodyRangeはループ処理の基本。見出し行が自動で除外される
- ListRows.Addで行追加、ListRows(i).Deleteで行削除(削除は下から!)
- ListColumns(“列名”).Indexで列番号を取得すれば、列の移動に強いコードになる
- ShowTotals + TotalsCalculationで集計行をワンクリック設定できる
「Range("A1:D100")のような”ベタ書き”から卒業して、テーブルベースのVBAを身につけると、メンテナンス性が格段に上がります。ぜひ今日から使ってみてください!」


コメント