【VBA】テーブル(ListObject)のデータをVBAで操作する方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク

完成イメージ(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)で保存する

  1. Excelを開く(新規でも既存でもOK)
  2. 「ファイル」→「名前を付けて保存」
  3. ファイルの種類を 「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

テーブルへの変換手順:

  1. データ範囲内の任意のセル(例:A1)をクリック
  2. Ctrl + T を押す
  3. 「先頭行をテーブルの見出しとして使用する」にチェックが入っていることを確認
  4. 「OK」をクリック

テーブル名を確認するには、テーブル内のセルを選択し、「テーブル デザイン」タブの左上を見る。既定では「テーブル1」になっている。


手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

Alt + F11 キーを押すとVBE(Visual Basic Editor)が開く。

一般的にはAlt + F11で開けるが、企業のセキュリティ設定でVBAが無効化されている場合は、IT部門に確認すること。

標準モジュールを挿入する

  1. VBE のメニュー「挿入」→「標準モジュール」をクリック
  2. 右側にコードウィンドウが表示される → ここにコードを貼り付ける

コードを貼り付ける

下の「コード(最小版)」をコピーして、標準モジュールのコードウィンドウに貼り付ける。

マクロを実行する

  1. VBEからExcelの画面に戻る(Alt + F11 または タスクバーでExcelをクリック)
  2. Alt + F8 キーを押す(マクロ一覧が表示される)
  3. 実行したいマクロ名を選んで「実行」をクリック

コード(最小版)– テーブルのデータ取得と行追加

まずはこれだけで動く。テーブルの基本情報を取得し、新しい行を追加する。


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

コードの動作:

  1. Sheet1 にテーブルがあるかチェック(なければ警告して終了)
  2. テーブルの基本情報(名前・行数・列数・見出し名)をメッセージボックスに表示
  3. テーブルの末尾に新しい行を追加
  4. 追加した行に日付・部署・商品名・売上を設定

ポイント:

  • 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).Rowlo.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指定で毎回最終行を計算する必要がなくなり、コードがシンプルになる。テーブルの列名で参照できるため、列の追加・並び替えにも強い。

関連記事


次にやりたくなること


もっとカスタマイズしたい場合

「複数テーブルを横断して集計したい」「テーブルのデータを別のExcelファイルに転記したい」「テーブルの構造ごとバックアップを取りたい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できます。

相談時に以下の情報があるとスムーズです:

  • Excel のバージョン / OS
  • テーブルの数とレイアウト(列名)
  • どのような集計・加工をしたいか
  • 出力先(同じシート / 別シート / 別ファイル)

コメント

タイトルとURLをコピーしました