
ループ内SQLをやめて高速化する実務テク

うさです🐰🌿
今回は、VBAでよくある「処理が遅い…」問題を、Dictionary(辞書)で解決する話です✨️
この記事のポイントはここ👇
ループの中でSQLを毎回発行していると、DBとの往復が行数分発生して、処理がかなり遅くなりやすいということ。

- ループの前で SQLを1回だけ実行
- SQLの取得結果を Dictionaryに全部入れる(キー→値の形にする)
- ループ中は Dictionary検索だけで値を取る
この形にすると、処理速度が体感で別物になる。
この記事では、いきなり難しいことはしないで、次の順で説明していくよ。
- まず「Dictionaryって何?」(キーで一発検索できる仕組み)
- 次に「よくある使い道3つ」(コード変換/重複集計/ループ内SQLの高速化)
- 最後に「本命:SQL結果をDictionaryに入れて、ループを高速化する実務サンプル」
読み終わるころには、「Dictionaryって怖い」じゃなくて、“遅い処理のボトルネックを外す道具”として使えるようになるはず。

ねこは全然わかんないので、読んでも「Dictionaryって怖い」ってなってます。
Dictionaryって何?(一言で)
VBAの Dictionary(辞書) は、
「キー(検索に使うID) → 値(取り出したいデータ)」を保存して、キーで一発検索できる仕組みです。
配列だと「何番目?」を探す。
Dictionaryだと「商品コード“12345”の情報ちょうだい」で終わる。

【58gの軽量デザイン】ほとんどのマウスの持ち方に対応した軽量デザインのRazer Cobraは、高速かつ高精度のコントロールを可能にするだけでなく、長時間のゲームでも非常に快適に使用できます。
【第3世代 Razer オプティカルマウススイッチ】デバウンス遅延のない0.2msの高速動作、チャタリングを起こさない9,000万回のクリック寿命など、他を圧倒する信頼性とスピードを持っています。
【アンダーグローグラデーションのChromaライティング】1,680万色のカラーオプションと無限のライティング効果を備え、数百のChroma対応ゲームでマウスをダイナミックに反応させることで、優れた没入感が体験できます。
【高精度のセンサー調整】Razer 8500 DPI オプティカルセンサーを搭載。50 DPI単位で高精度の調整を行うことができ、プレイスタイルに合わせた完璧な設定が可能です。
Dictionaryの強い使い道(実務でよくある3つ)
1) コード変換(商品コード→商品名など)
マスタを辞書化して、明細の変換を高速化。
2) 重複集計(同じキーをまとめる)
商品コード別の数量集計、得意先別の件数集計など。
3) ループ内SQLをやめて高速化(本命)
明細1行ごとにSQL発行してると、DB往復が発生して激遅になります。
そこで、ループの前でSQLを1回だけ実行し、結果をDictionaryに全部入れておく。
あとはループ中はDictionary検索だけにすれば、桁違いに速くなります。
まずは超基本:Dictionaryの最小セット
実務でまず使うのはこの4つだけでOK。
dic(key) = value:追加/更新(実務はこれが一番安全)dic.Exists(key):存在チェックdic(key):値の取得dic.Count:件数
使い道の例①:商品コード別に数量を集計(重複をまとめる)
(A列:商品コード / B列:数量)
Option Explicit
Sub SUB_SAMPLE_SUM_BY_CODE()
Dim l_objDic As Object
Dim l_LngLastRow As Long
Dim l_LngRow As Long
Dim l_StrCode As String
Dim l_LngQty As Long
Set l_objDic = CreateObject("Scripting.Dictionary")
With ActiveSheet
l_LngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For l_LngRow = 2 To l_LngLastRow
l_StrCode = CStr(.Cells(l_LngRow, "A").Value)
l_LngQty = CLng(.Cells(l_LngRow, "B").Value)
If l_objDic.Exists(l_StrCode) Then
l_objDic(l_StrCode) = CLng(l_objDic(l_StrCode)) + l_LngQty
Else
l_objDic(l_StrCode) = l_LngQty
End If
Next
End With
Dim l_VarKey As Variant
For Each l_VarKey In l_objDic.Keys
Debug.Print l_VarKey & " = " & l_objDic(l_VarKey)
Next
End Sub
使い道の例②:マスタ変換(コード→名称)を高速化
商品マスタを辞書にして、明細ループで引くだけ。
Option Explicit
Private Function FNC_GET_SHOUHIN_DIC(ByVal p_ObjMstSheet As Worksheet) As Object
Dim l_objDic As Object
Dim l_LngLastRow As Long
Dim l_LngRow As Long
Dim l_StrCode As String
Dim l_StrName As String
Set l_objDic = CreateObject("Scripting.Dictionary")
With p_ObjMstSheet
l_LngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For l_LngRow = 2 To l_LngLastRow
l_StrCode = CStr(.Cells(l_LngRow, "A").Value) ' 商品コード
l_StrName = CStr(.Cells(l_LngRow, "B").Value) ' 商品名
l_objDic(l_StrCode) = l_StrName ' 追加/更新
Next
End With
Set FNC_GET_SHOUHIN_DIC = l_objDic
End Function

販売名:アキュビュー オアシス / 承認番号:21800BZY10252000 / 登録商標 原産国:アイルランド共和国 / アメリカ合衆国
DIA(直径):14.0mm 、BC(ベースカーブ):8.8 / 8.4mm、PWR:-0.50 ~ -6.00(0.25ステップ)、-6.50 ~ -12.00(0.50ステップ)、+0.50 ~ +5.00(0.25ステップ)
2週間頻回交換コンタクトレンズ / 1箱6枚入り / 両目6週間分/酸素透過率147DK/L / 含水率38%
コンタクトレンズの乾きから自由になることを目指したアキュビュー オアシス。それを可能にしたのは、次世代素材「シリコーンハイドロゲル」*と、独自の技術「ハイドラクリアプラス・テクノロジー」*の融合。変わらないみずみずしさと目の健康への思いが実を結んだ、コンタクトレンズの新しいカタチ。 ※ レンズ素材名:セノフィルコンA ※ Johnson&Johnson社独自のテクノロジー名。
使い道の例③(本命):ループ内SQLをやめてDictionaryで高速化する
ここが一番「効く」使い方。
NG例:ループの中でSQLを毎回発行(遅い)
明細が1000行なら、SQLも1000回。
DB往復が一番遅いので、これがボトルネックになりがち。
' ※概念例(この構造が遅い)
For l_LngRow = 2 To l_LngLastRow
l_StrCode = CStr(.Cells(l_LngRow, "A").Value)
' ここで毎回SQL → 遅い(往復が積み上がる)
l_StrSql = "SELECT 商品名 FROM 商品マスタ WHERE 商品コード = '" & l_StrCode & "'"
' Recordset.Open l_StrSql, ...
' .Cells(l_LngRow, "B").Value = rs!商品名
Next
※文字列連結でSQLを作る方法は、条件値の扱いによってはセキュリティ面でも注意が必要です。ここではDictionaryによる高速化の説明に絞っています。
OK例:ループの前にSQL結果をDictionaryへ読み込む(速い)
やり方はシンプル:
- ループ前に SQLを1回だけ発行
- 取得結果を
dic(商品コード)=商品名の形で全部入れる - ループ中は
dic(code)を引くだけ(DBアクセスしない)
① SQL結果をDictionary化する関数(ADO想定)
※ADO参照設定なしでも動きやすいように、CreateObjectで書いています。実務では環境に合わせて接続方法を調整してください。
Option Explicit
Private Function FNC_GET_SHOUHIN_FROM_DB_DIC(ByVal p_ObjCn As Object) As Object
On Error GoTo ERROR_HANDLER
Dim l_objDic As Object
Dim l_ObjRs As Object
Dim l_StrSql As String
Dim l_StrCode As String
Dim l_StrName As String
Set l_objDic = CreateObject("Scripting.Dictionary")
' 例:必要なマスタを一括取得(条件があるなら WHERE で絞る)
l_StrSql = ""
l_StrSql = l_StrSql & "SELECT 商品コード, 商品名 "
l_StrSql = l_StrSql & "FROM 商品マスタ "
Set l_ObjRs = CreateObject("ADODB.Recordset")
l_ObjRs.Open l_StrSql, p_ObjCn
Do While l_ObjRs.EOF = False
l_StrCode = Format$(CLng(l_ObjRs.Fields(0).Value), "00000")
If IsNull(l_ObjRs.Fields(1).Value) Then
l_StrName = ""
Else
l_StrName = CStr(l_ObjRs.Fields(1).Value)
End If
l_objDic(l_StrCode) = l_StrName ' 追加/更新
l_ObjRs.MoveNext
Loop
l_ObjRs.Close
Set l_ObjRs = Nothing
Set FNC_GET_SHOUHIN_FROM_DB_DIC = l_objDic
Exit Function
ERROR_HANDLER:
If Not l_ObjRs Is Nothing Then
If l_ObjRs.State <> 0 Then l_ObjRs.Close
Set l_ObjRs = Nothing
End If
Set FNC_GET_SHOUHIN_FROM_DB_DIC = Nothing
End Function
② 明細ループはDictionary検索だけ
Option Explicit
Sub SUB_SAMPLE_FAST_LOOKUP()
On Error GoTo ERROR_HANDLER
Dim l_ObjCn As Object
Dim l_ObjDic As Object
Dim l_LngLastRow As Long
Dim l_LngRow As Long
Dim l_StrCode As String
' DB接続は環境に合わせて設定する
Set l_ObjCn = CreateObject("ADODB.Connection")
l_ObjCn.ConnectionString = "接続文字列を設定"
l_ObjCn.Open
' ★ ここがポイント:ループ前に辞書を作る
Set l_ObjDic = FNC_GET_SHOUHIN_FROM_DB_DIC(l_ObjCn)
If l_ObjDic Is Nothing Then
MsgBox "商品マスタの取得に失敗しました。", vbExclamation
GoTo EXIT_PROC
End If
With ThisWorkbook.Worksheets("明細")
l_LngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For l_LngRow = 2 To l_LngLastRow
l_StrCode = Format$(CLng(.Cells(l_LngRow, "A").Value), "00000")
If l_ObjDic.Exists(l_StrCode) Then
.Cells(l_LngRow, "B").Value = CStr(l_ObjDic(l_StrCode))
Else
.Cells(l_LngRow, "B").Value = "(未登録)"
End If
Next
End With
EXIT_PROC:
If Not l_ObjCn Is Nothing Then
If l_ObjCn.State <> 0 Then
l_ObjCn.Close
End If
End If
Set l_ObjDic = Nothing
Set l_ObjCn = Nothing
Exit Sub
ERROR_HANDLER:
MsgBox "エラーが発生しました。" & vbCrLf & _
"エラー番号:" & Err.Number & vbCrLf & _
"内容:" & Err.Description, _
vbExclamation
Resume EXIT_PROC
End Sub
なぜ速くなるの?(理由はDBの往復)
- ループ内SQL:行数分、DB往復
- Dictionary方式:DB往復は1回(もしくは少数回)+ ループはメモリ参照だけ
- メモリ参照(Dictionary検索)は、DB往復より圧倒的に軽いので、行数が増えるほど差が出ます。
※ただし、マスタが巨大で明細件数が少ない場合は、明細側の商品コードを重複排除したうえで、IN句、JOIN、作業テーブルなどを使用し、必要なデータだけを1回または少数回のSQLで取得すると省メモリです。原則として、明細1行ごとのSQL発行は避けます。Dictionary化する範囲は、「明細件数」と「マスタ件数」のバランスを見て判断してください。
落とし穴(実務で事故りやすいポイント)
1) キーは型ブレさせない(基本は文字列)
Excel側とDB側で、00123と123のように形式が異なると、同じコードでもDictionaryのキーが一致しません。
商品コードが5桁固定の場合は、Dictionaryへ登録する側と検索する側の両方を、同じ5桁形式に統一します。
l_StrCode = Format$(CLng(value), “00000”)
2) 取得件数が多すぎる場合はSQLで絞る
全件マスタが巨大なら、必要な範囲だけ取得します。WHEREで対象データを絞り、SELECTする列も必要なものだけにします。必要に応じてJOINも使用します。
3) 「存在しない」ケースを必ず処理
未登録コードは現場で普通に出るので、Exists は実務では必須。
まとめ:Dictionaryは「検索のためのメモリDB」
- 配列より読みやすい
- コード変換・集計に強い
- ループ内SQLを減らせる場面では、体感で別世界に速くなることがあります

アクティブノイズキャンセリング ̶ アクティブノイズキャンセリング搭載 AirPods 4 は、あなたの耳に届く前に外部からの雑音を低減。いま聴いているものに夢中になれま す*。
周囲の様子を聞ける ̶ AirPods 4 はパワフルな H2 チップを内蔵。適応型オーディオ がアクティブノイズキャンセリングと外部音取り込みモードをシームレスに融合するの で、周囲の音が正確かつ快適に聞こえます。周りの様子もわかるので、あらゆる環境で 最高のリスニング体験を楽しめます*。近くにいる人と話している時は、会話感知機能 が再生中のオーディオの音量を自動的に下げます*。
さらに優れた音質と通話品質 ̶ 「声を分離」機能は、雑音の大きい場所での通話品質 を向上させます。先進的なコンピュテーショナルオーディオを活用して、周囲の雑音を 低減。あなたの声を分離して、クリアな音声にしながら通話の相手に届けます*。
魔法のような体験 ̶「Hey Siri」と話しかけるだけで、曲の再生、電話の発信、スケ ジュールのチェックが思いのまま*。これからは、「はい」ならうなずく、「いいえ」 なら首を横に振るだけで Siri に応答できます*。ペアリングしたい時は、AirPods 4 をあ なたのデバイスの近くに置き、画面上で「接続」をタップするだけ*。2 組の AirPods で音楽や映画を共有するのも簡単です*。肌検出センサーがオーディオを再生するタイ ミングを識別するので、あなたが AirPods をつけている時だけ再生し、外すと一時停 止します。「探す」アプリで AirPods と充電ケースを探すこともできます*。
パーソナライズされた空間オーディオ ̶ パーソナライズされた空間オーディオとダイ ナミックヘッドトラッキングが、あなたの周りに音を配置。音楽、テレビ番組、映画、 ゲームなどに映画館のようなリスニング体験をもたらします*。
再設計されたケース ̶ 充電ケースは、ワイヤレス充電対応モデルでは業界最小のサイ ズ。充電には Apple Watch の充電器、USB-C 充電ケーブル、Qi 規格の充電器が使えま す*。
長時間持続するバッテリー - アクティブノイズキャンセリングを有効にすると、1 回 の充電で最大 4 時間、充電ケースを使えば最大 20 時間再生できます。アクティブノイ ズキャンセリングを使わない場合は、合計再生時間が最大 30 時間になります*。
防塵性能と耐汗耐水性能 - AirPods 4 と充電ケースは、IP54 等級の防塵性能と耐汗耐 水性能を持っています。激しいワークアウトや雨で濡れても安心です*。
*免責事項 ̶ これらは製品の主な機能の概要です。詳しくは以下をご覧ください。
免責事項
本記事のサンプルプログラムは、学習・参考用として掲載しているもので、動作や結果を保証するものではありません。 利用する場合は、ご自身の環境に合わせて確認しながらお使いください。万が一トラブルや損害が発生した場合でも、当サイトでは責任を負いかねます。





































※本文中に記載の会社名・製品名・サービス名・ゲームタイトル名等は、各社の商標または登録商標であり、権利は各社に帰属します。
※サンプルはテストを行っていますが、すべての環境での動作を保証するものではありません。ご利用は自己責任でお願いいたします。
※本記事の仕様・価格・対応状況等は執筆時点で確認できた情報をもとに掲載しています。最新の情報はメーカー公式サイトをご確認ください。
※当サイトでは一部の記事において、アイキャッチ画像にAI生成を使用しています。
※Amazonのアソシエイトとして、うさねこ散歩は適格販売により収入を得ています。