この記事でわかること
- 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で条件を変えながら手作業で抽出):
- AutoFilterで「東京支店」を選択 → 結果をコピー → 別シートに貼り付け
- フィルタ解除 → 「大阪支店」を選択 → コピー → 貼り付け
- フィルタ解除 → 「売上100万以上」で絞り込み → コピー → …
- 条件が増えるたびに繰り返し
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 のままだとマクロが保存できない。
- 「ファイル」→「名前を付けて保存」
- ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
- 保存
参照設定は不要
このコードは CreateObject を使った遅延バインディングで書いている。VBEの参照設定を変更する必要はない。配布先のPCでもそのまま動く。
Option Explicitについて
標準モジュールの先頭に Option Explicit を入れておくと、変数名のタイプミスをコンパイル時に検出できる。VBEメニュー →「ツール」→「オプション」→「変数の宣言を強制する」にチェックを入れておくと、新規モジュールに自動で追加される。
—
手順(コピペ → 実行まで約5分)
VBE(コードを書く画面)を開く
- Excelで
Alt + F11を押す
標準モジュールを挿入する
- VBEのメニュー →「挿入」→「標準モジュール」
コードを貼り付けて実行する
- コードウィンドウに、下のコードをそのままコピペする
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=Yes を HDR=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.Close→cn.Closeを必ず実行する(エラー時も)
関連記事
- 複数条件でデータを抽出して別シートにまとめる方法 — AutoFilterでの抽出。条件が少ない場合はこちらがシンプル
- エラー処理(On Error)で止まらないマクロを作る方法 — ADO接続エラーのハンドリング
- 配列を使ってVBAの処理速度を10倍にする方法 — ADOで取得したデータの高速加工
- Dictionaryで重複チェック・集計を高速化する方法 — SQL集計との使い分け
- CSVファイルをExcelに正しく取り込む方法 — ADOを使った外部データ取込の別パターン
—
次にやりたくなること
- 複数ブックを開かずにデータ取得する方法: ADOを使えば、閉じたままのブックからもデータを取得できる。複数ファイルからの集約に便利
- Dictionaryで重複チェック・集計を高速化する方法: SQL集計と組み合わせて、VBA内でさらに細かい集計や重複チェックをしたい場合
- オートフィルタでデータを絞り込み・解除する方法: 条件が少ない場合はAutoFilterの方が手軽。SQL検索との使い分けの参考に


コメント