【VBA】ADO/SQLでExcelシートをデータベースのように操作する方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク
  1. この記事でわかること
    1. どんな場面で使う?
  2. 完成イメージ(Before / After)
  3. 実行前の準備
    1. バックアップを取る
    2. Excelをマクロ有効ブック(.xlsm)で保存する
    3. 参照設定は不要
    4. Option Explicitについて
  4. 手順(コピペ → 実行まで約5分)
    1. VBE(コードを書く画面)を開く
    2. 標準モジュールを挿入する
    3. コードを貼り付けて実行する
  5. コード(最小版)– SQLでシートからデータを取得する
    1. 書き換えポイント
    2. 接続文字列のパラメータ
  6. コード(実務版)– 条件抽出+別シート出力+エラー処理付き
    1. 書き換えポイント
    2. シート構成の例
    3. SQL文の書き方(よく使うパターン)
  7. よくある落とし穴5選
    1. 1. 「外部テーブルが見つかりません」エラー
    2. 2. ヘッダー行がデータとして取り込まれる(HDR設定ミス)
    3. 3. 数値列がSQLで文字列として扱われる(IMEX設定)
    4. 4. 自ブックに接続すると未保存データが取れない
    5. 5. 列名にスペースや記号があるとSQL構文エラー
    6. VBAのADOで外部テーブルが見つかりませんエラーが出るときの対処法
    7. VBAでADOのSQLが数値比較できないときの対処法
  8. FAQ
    1. Q1: 参照設定は必要?
    2. Q2: .xlsxファイル(マクロなしブック)のデータも取得できる?
    3. Q3: 閉じているブックのデータも取得できる?
    4. Q4: UPDATE文やDELETE文でデータを書き換えられる?
    5. Q5: 「Microsoft.ACE.OLEDB.12.0 プロバイダーは登録されていません」エラーが出る
  9. まとめ
    1. 関連記事
  10. 次にやりたくなること

この記事でわかること

  • VBAからADO(ActiveX Data Objects)でExcelシートに接続し、SQLのSELECT文でデータを抽出できる
  • WHERE / GROUP BY / ORDER BY を使った条件抽出・集計・並べ替えがコピペで動く
  • 参照設定なし(遅延バインディング)で動作するので、配布先のPCでもそのまま使える

対象: Excel 2016以降 / Microsoft 365、Windows 10/11

どんな場面で使う?

  • 数万行の売上データからSQLで条件抽出・集計を一発で済ませたいとき
  • AutoFilterで何度もフィルタをかけ直すのが面倒なとき
  • 月次レポートの元データ作成をGROUP BYで自動集計したいとき
  • 閉じたブックのデータをSQLで取得して効率化したいとき

完成イメージ(Before / After)

Before(AutoFilterで条件を変えながら手作業で抽出):

  1. AutoFilterで「東京支店」を選択 → 結果をコピー → 別シートに貼り付け
  2. フィルタ解除 → 「大阪支店」を選択 → コピー → 貼り付け
  3. フィルタ解除 → 「売上100万以上」で絞り込み → コピー → …
  4. 条件が増えるたびに繰り返し

After(SQL1行で条件抽出):


SELECT 支店名, 商品名, 売上金額
FROM [売上データ$]
WHERE 支店名 = '東京支店' AND 売上金額 >= 1000000
ORDER BY 売上金額 DESC

→ 条件に合うデータが別シートに一覧出力される。条件を変えたいときはSQL文を書き換えるだけ。

自分も以前、数万行の売上一覧からAutoFilterで条件を変えながら抽出→別シートにコピペを繰り返していた。条件が5つ以上になると、フィルタのかけ直しだけで午前中が潰れることもあって、正直めんどくさかった。ADO/SQLを覚えてからは、SELECT文1行で条件抽出・集計・並べ替えが一発で終わるようになった。月次レポートの元データ作成が30分から3分に縮まった。同じようにAutoFilterの限界で苦労している人が、この記事でSQL検索をサクッと使えるようになればうれしい。

ExcelシートにSQLを実行すれば、条件抽出・集計・並べ替えが1行で完結する。

なお、AutoFilterを使った抽出方法は 複数条件でデータを抽出して別シートにまとめる方法 で解説している。条件が少ない場合はAutoFilterの方がシンプルなので、使い分けの参考にしてほしい。

実行前の準備

バックアップを取る

実務版コードは別シートにデータを書き出す。元データは変更しないが、念のためファイルのコピーを別フォルダに保存してから実行する。

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロが保存できない。

  1. 「ファイル」→「名前を付けて保存」
  2. ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  3. 保存

参照設定は不要

このコードは CreateObject を使った遅延バインディングで書いている。VBEの参照設定を変更する必要はない。配布先のPCでもそのまま動く。

Option Explicitについて

標準モジュールの先頭に Option Explicit を入れておくと、変数名のタイプミスをコンパイル時に検出できる。VBEメニュー →「ツール」→「オプション」→「変数の宣言を強制する」にチェックを入れておくと、新規モジュールに自動で追加される。

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

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

  1. Excelで Alt + F11 を押す

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

  1. VBEのメニュー →「挿入」→「標準モジュール」

コードを貼り付けて実行する

  1. コードウィンドウに、下のコードをそのままコピペする
  2. Alt + F8 → マクロ名を選んで「実行」

コード(最小版)– SQLでシートからデータを取得する

セルのデータに対してSELECT文を実行し、結果をイミディエイトウィンドウに出力する。まずはこれで動きを確認する。

イミディエイトウィンドウは VBE上で Ctrl + G を押すと画面下部に表示される。

前提: アクティブブックの「Sheet1」にヘッダー行付きのデータがあること。ファイルが保存済みであること(未保存だとADOが読み取れない)。


'============================================================
' ■ ADO/SQLでExcelシートからデータ取得(最小版)
'   → Sheet1に対してSELECT文を実行し、結果を出力
'   → 参照設定不要(遅延バインディング)
'============================================================
Sub ADO_SelectBasic()

    '--- ★書き換えポイント ---
    Dim filePath As String
    filePath = ThisWorkbook.FullName       '← 対象ファイル(自ブック)

    Dim sheetName As String
    sheetName = "Sheet1"                   '← 対象シート名

    Dim sql As String
    sql = "SELECT * FROM [" & sheetName & "$]"  '← SQL文
    '--- ★ここまで ---

    '--- ADO接続を作成
    Dim cn As Object
    Set cn = CreateObject("ADODB.Connection")

    '--- 接続文字列を組み立て
    '    IMEX=1: 全列を文字列として読み取る(型混在の問題を回避)
    '    まずはIMEX=1で確実にデータを取得し、動作確認する
    Dim connStr As String
    connStr = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
              "Data Source=" & filePath & ";" & _
              "Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"

    '--- 接続を開く
    cn.Open connStr

    '--- SQLを実行してRecordsetを取得
    Dim rs As Object
    Set rs = cn.Execute(sql)

    '--- ループで使う変数を宣言
    Dim i As Long
    Dim line As String

    '--- 結果をイミディエイトウィンドウに出力(Ctrl+Gで表示)
    Do Until rs.EOF
        line = ""
        For i = 0 To rs.Fields.Count - 1
            If i > 0 Then line = line & vbTab
            line = line & rs.Fields(i).Value
        Next i
        Debug.Print line
        rs.MoveNext
    Loop

    '--- 後片付け(必ず閉じる)
    rs.Close
    cn.Close
    Set rs = Nothing
    Set cn = Nothing

    MsgBox "取得完了。イミディエイトウィンドウ(Ctrl+G)で結果を確認してください。", vbInformation

End Sub

書き換えポイント

変数 説明 初期値
filePath 対象Excelファイルのフルパス ThisWorkbook.FullName(自ブック)
sheetName 対象シート名 "Sheet1"
sql 実行するSQL文 SELECT * FROM [Sheet1$](全件取得)

ポイント: [シート名$]$ と角括弧は必須。これがないと「外部テーブルが見つかりません」エラーになる。

接続文字列のパラメータ

パラメータ 意味
Provider Microsoft.ACE.OLEDB.12.0 Excel 2007以降のファイル用プロバイダ
Data Source ファイルのフルパス 接続先のExcelファイル
HDR Yes 1行目をヘッダー(列名)として扱う
IMEX 1 全列を文字列として読み取る(型混在の問題を回避)

重要: HDR=Yes は「1行目をヘッダーとして扱う」設定。1行目からデータが入っている場合は HDR=No に変更する。HDR=No の場合、列名は F1, F2, F3… という自動名になる。

コード(実務版)– 条件抽出+別シート出力+エラー処理付き

売上一覧表に対してSQLで条件抽出し、結果を「抽出結果」シートに書き出す。エラー処理付きで、ADO接続の閉じ忘れを防ぐ。

このSQL検索パターンを覚えてからは、Dictionaryで手動集計していた月次レポートもSQLのGROUP BYで一発集計に切り替えた。コードが半分以下になって保守も楽になった。もっと早く知りたかった。

実務版では IMEX=0 を使用している。 最小版の IMEX=1(全列文字列)と異なり、IMEX=0 は数値列を数値として扱う。WHERE句での数値比較(売上金額 >= 500000)やSUM/AVG集計を正しく実行するために必要な設定。

※ 「抽出結果」シートの既存データはクリアされます。実行前にバックアップを取ってください。


'============================================================
' ■ ADO/SQLで条件抽出+別シート出力(実務版)
'   → 「売上データ」シートからSQLで条件抽出
'   → 「抽出結果」シートに結果を書き出し
'   → エラー処理付き・ADO接続の閉じ忘れ防止
'   → 参照設定不要(遅延バインディング)
'============================================================
Sub ADO_SelectToSheet()

    '--- ★書き換えポイント ---
    Dim filePath As String
    filePath = ThisWorkbook.FullName           '← 対象ファイル

    Dim srcSheet As String
    srcSheet = "売上データ"                     '← データ元のシート名

    Dim dstSheet As String
    dstSheet = "抽出結果"                       '← 結果の出力先シート名

    Dim sql As String
    sql = "SELECT 支店名, 商品名, 売上金額, 売上日 " & _
          "FROM [" & srcSheet & "$] " & _
          "WHERE 売上金額 >= 500000 " & _
          "ORDER BY 売上金額 DESC"
    '--- ★ここまで ---

    '--- ADOオブジェクトを事前宣言(エラー時のCleanup用)
    Dim cn As Object
    Dim rs As Object

    On Error GoTo ErrHandler

    '--- 自ブック接続の場合、未保存データを保存
    '    ADOは「保存済みのファイル」に接続するため、未保存の変更は見えない
    '    自動保存が不安な場合は、別ブックのデータを対象にすることを推奨
    If filePath = ThisWorkbook.FullName Then
        If Not ThisWorkbook.Saved Then
            ThisWorkbook.Save
        End If
    End If

    '--- ADO接続を作成
    Set cn = CreateObject("ADODB.Connection")

    '--- 接続文字列(IMEX=0: 数値を数値として扱う → WHERE/SUM/AVGが正しく動作)
    Dim connStr As String
    connStr = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
              "Data Source=" & filePath & ";" & _
              "Extended Properties=""Excel 12.0;HDR=Yes;IMEX=0"";"

    cn.Open connStr

    '--- SQLを実行
    Set rs = cn.Execute(sql)

    '--- 結果が空かチェック
    If rs.EOF Then
        MsgBox "条件に一致するデータがありませんでした。" & vbCrLf & _
               "SQL: " & sql, vbExclamation
        GoTo Cleanup
    End If

    '--- 出力先シートを準備
    Dim wsDst As Worksheet
    On Error Resume Next
    Set wsDst = ThisWorkbook.Worksheets(dstSheet)
    On Error GoTo ErrHandler

    If wsDst Is Nothing Then
        '--- シートがなければ新規作成
        Set wsDst = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
        wsDst.Name = dstSheet
    Else
        '--- 既存データをクリア
        wsDst.Cells.Clear
    End If

    '--- ヘッダー行を書き出し
    Dim col As Long
    For col = 0 To rs.Fields.Count - 1
        wsDst.Cells(1, col + 1).Value = rs.Fields(col).Name
    Next col

    '--- ヘッダー行を太字にする
    wsDst.Rows(1).Font.Bold = True

    '--- データを一括書き出し(CopyFromRecordsetで高速)
    wsDst.Range("A2").CopyFromRecordset rs

    '--- 列幅を自動調整
    wsDst.Columns.AutoFit

    '--- 処理件数を取得(CopyFromRecordset後はrs.RecordCountが使えないため、最終行から計算)
    Dim lastRow As Long
    lastRow = wsDst.Cells(wsDst.Rows.Count, 1).End(xlUp).Row

    MsgBox (lastRow - 1) & " 件のデータを「" & dstSheet & "」シートに出力しました。", vbInformation

Cleanup:
    '--- 後片付け(エラーが起きても必ず実行)
    On Error Resume Next
    If Not rs Is Nothing Then
        If rs.State <> 0 Then rs.Close  '0 = adStateClosed(閉じた状態)
        Set rs = Nothing
    End If
    If Not cn Is Nothing Then
        If cn.State <> 0 Then cn.Close  '0 = adStateClosed(閉じた状態)
        Set cn = Nothing
    End If
    On Error GoTo 0
    Exit Sub

ErrHandler:
    MsgBox "エラーが発生しました。" & vbCrLf & _
           "エラー番号: " & Err.Number & vbCrLf & _
           "内容: " & Err.Description & vbCrLf & vbCrLf & _
           "SQL: " & sql, vbCritical
    Resume Cleanup

End Sub

書き換えポイント

変数 説明 初期値
filePath 対象Excelファイルのフルパス ThisWorkbook.FullName(自ブック)
srcSheet データ元のシート名 "売上データ"
dstSheet 結果の出力先シート名 "抽出結果"
sql 実行するSQL文 売上金額50万以上を降順で取得

シート構成の例

A列(支店名) B列(商品名) C列(売上金額) D列(売上日)
東京支店 商品A 1200000 2026/01/15
大阪支店 商品B 800000 2026/01/20
東京支店 商品C 300000 2026/02/01
名古屋支店 商品A 550000 2026/02/10

→ SQL実行後、売上金額50万以上の3件が「抽出結果」シートに降順で出力される。

SQL文の書き方(よく使うパターン)


-- 全件取得
SELECT * FROM [売上データ$]

-- 条件抽出(WHERE)
SELECT * FROM [売上データ$] WHERE 支店名 = '東京支店'

-- 複数条件(AND / OR)
SELECT * FROM [売上データ$]
WHERE 支店名 = '東京支店' AND 売上金額 >= 500000

-- 並べ替え(ORDER BY)
SELECT * FROM [売上データ$] ORDER BY 売上金額 DESC

-- 集計(GROUP BY + SUM)
SELECT 支店名, SUM(売上金額) AS 合計売上
FROM [売上データ$]
GROUP BY 支店名

-- 件数カウント
SELECT 支店名, COUNT(*) AS 件数
FROM [売上データ$]
GROUP BY 支店名

-- 上位N件(TOP)
SELECT TOP 10 * FROM [売上データ$] ORDER BY 売上金額 DESC

-- 重複除外(DISTINCT)
SELECT DISTINCT 支店名 FROM [売上データ$]

-- 範囲指定(特定のセル範囲だけ対象にする)
-- ※ 行数が固定されるため、データ件数が変動する場合は [シート名$] を推奨
SELECT * FROM [売上データ$A1:D1000]

エラー処理の考え方は エラー処理(On Error)で止まらないマクロを作る方法 を参照。

大量データの処理速度をさらに上げたい場合は 配列を使ってVBAの処理速度を10倍にする方法 も参考になる。

よくある落とし穴5選

1. 「外部テーブルが見つかりません」エラー

症状: 実行時に「外部テーブルが見つかりません」というランタイムエラーが出る。

原因: SQL文のシート名指定が間違っている。$ を付け忘れている、または角括弧で囲んでいない。

対策: シート名は必ず [シート名$] の形式で指定する。


' NG
sql = "SELECT * FROM Sheet1"       '← $がない
sql = "SELECT * FROM Sheet1$"      '← 角括弧がない

' OK
sql = "SELECT * FROM [Sheet1$]"    '← $と角括弧が必須

2. ヘッダー行がデータとして取り込まれる(HDR設定ミス)

自分もこれでハマった。接続文字列の HDR=YesHDR=No にしたまま気づかず、ヘッダー行(「支店名」「売上金額」など)がデータとして取り込まれて集計結果がズレた。上司に提出した月次集計が合計値ズレで差し戻しになった。原因に気づいたときは「なんでこんな設定を見落としたんだ」と正直凹んだ。

原因: HDR=No だと1行目もデータ行として扱われる。列名は F1, F2, … という自動名になる。

対策: 1行目がヘッダーなら HDR=Yes、1行目からデータなら HDR=No を明示する。接続文字列を変更したら、必ず少量データで動作確認してから本番実行する。

3. 数値列がSQLで文字列として扱われる(IMEX設定)

症状: WHERE 売上金額 >= 500000 が効かない。SUM/AVGの結果が0になる。

原因: IMEX=1 は全列を文字列として読み取る設定。数値の比較や集計ができない。

対策: 数値として扱いたい場合は IMEX=0 にする。列にテキストと数値が混在している場合は IMEX=1 にして、VBA側で CDbl() で変換する。

IMEX値 動作 使い分け
0 読み書きモード(型を自動判定) 数値の比較・集計が必要な場合
1 読み取りモード(全列文字列) まずデータを確実に取得したい場合

4. 自ブックに接続すると未保存データが取れない

症状: セルの値を変更してからマクロを実行したのに、変更前のデータが取得される。

原因: ADOは「保存済みのファイル」に接続する。未保存の変更はADOからは見えない。

対策: ADO接続前に ThisWorkbook.Save で保存する(実務版コードにはこの処理を入れてある)。自動保存が不安な場合は、別ブックのデータを対象にするか、保存前にMsgBoxで確認を入れるとよい。

5. 列名にスペースや記号があるとSQL構文エラー

症状: 「構文エラー」でSELECT文が実行できない。

原因: 列名(ヘッダー)に半角スペース、/-# などが含まれると、SQLの予約語や構文と競合する。

対策: 列名を角括弧で囲む。


-- NG
SELECT 売上 金額 FROM [Sheet1$]      -- スペースでエラー

-- OK
SELECT [売上 金額] FROM [Sheet1$]    -- 角括弧で囲む

VBAのADOで外部テーブルが見つかりませんエラーが出るときの対処法

「SELECT文を実行したら外部テーブルが見つかりませんと出る」という場合、原因はシート名の指定形式の間違いだ。シート名は [シート名$] のように角括弧と$を付ける必要がある。角括弧や$を省略するとエラーになる。

VBAでADOのSQLが数値比較できないときの対処法

「WHERE 売上金額 >= 500000 が効かない」という場合、原因は接続文字列のIMEX設定だ。IMEX=1は全列を文字列として扱うため数値比較ができない。数値の比較や集計が必要な場合はIMEX=0に変更する。

FAQ

Q1: 参照設定は必要?

不要。このコードは CreateObject("ADODB.Connection") を使った遅延バインディングで書いている。参照設定を変更しなくても動く。配布先のPCでもそのまま使える。

ただし、VBEで入力補完(IntelliSense)を使いたい場合は、VBEメニュー →「ツール」→「参照設定」→「Microsoft ActiveX Data Objects 6.1 Library」にチェックを入れる。

Q2: .xlsxファイル(マクロなしブック)のデータも取得できる?

できる。接続文字列の Provider=Microsoft.ACE.OLEDB.12.0 は .xlsx / .xlsm / .xlsb に対応している。対象ファイルのパスを Data Source に指定すればOK。

Q3: 閉じているブックのデータも取得できる?

できる。ADOはファイルパスを指定して接続するので、ブックを開かなくてもデータを取得できる。これがADOの大きなメリットの1つ。閉じたブックからのデータ取得は 複数ブックを開かずにデータ取得する方法 でも解説している。

Q4: UPDATE文やDELETE文でデータを書き換えられる?

技術的にはINSERT/UPDATE/DELETEも実行できるが、Excelシートに対する書き換えは予期しないデータ破損のリスクがある。SELECTで取得 → VBAでシートに書き戻す方が安全。 この記事ではSELECT(読み取り)だけを扱う。

Q5: 「Microsoft.ACE.OLEDB.12.0 プロバイダーは登録されていません」エラーが出る

64bit版Excelに対して32bit版のACEドライバがインストールされている(またはその逆)場合に発生する。Microsoft 365やOffice 2016以降がインストール済みであれば通常は発生しない。エラーが出た場合は、Microsoft公式サイトから「Access Database Engine Redistributable」をExcelと同じビット数でダウンロード・インストールする。Excelのビット数は「ファイル」→「アカウント」→「Excelのバージョン情報」で確認できる。

まとめ

  • ADO + SQL: ExcelシートにSQLのSELECT文を実行して、条件抽出・集計・並べ替えができる
  • 接続文字列: Provider=Microsoft.ACE.OLEDB.12.0 + HDR=Yes(ヘッダーあり)が基本
  • シート名の指定: [シート名$] の形式($ と角括弧が必須)
  • IMEX設定: 数値比較・集計には IMEX=0、まず確実に取得するなら IMEX=1
  • 参照設定不要: CreateObject で遅延バインディングすれば、配布先でもそのまま動く
  • 後片付け必須: rs.Closecn.Close を必ず実行する(エラー時も)

関連記事

次にやりたくなること

コメント

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