【技術コラム】ワークテーブル廃止!「ADO切断+ディクショナリ方式」で作る超高速・一覧編集フォームの極意
ファイルサーバー上にバックエンド用のAccessファイル(.accdb)を配置し、複数人でフロントエンドから共有利用するシステムにおいて、
・一覧編集フォームの表示が重い
・スクロールが引っかかる
・Wi-FiやVPN環境で固まる
・ファイル破損やロック競合が頻発する
といった問題に悩まされた経験はないでしょうか。
特に、大量データを扱う「一覧形式の編集フォーム」では、従来の実装方法に限界があります。
今回は、ローカルのワークテーブル(一時テーブル)を一切作成せず、Accessのパフォーマンスを極限まで引き出すアーキテクチャ
「ADO切断+ディクショナリ方式」について、実践的な3つの極意をご紹介します。
1. 従来手法と「超えられない壁」
Accessで一覧編集フォームを作る際、一般的なのは次のような構成です。
1.バックエンドからデータを取得
2.フロントエンド内にワークテーブルを作成
3.そのワークテーブルをフォームへ連結(バインド)
この方式は安全で扱いやすい反面、データ件数が増えると深刻な問題が発生します。
■ ワークテーブル作成が重い
数万件規模になると、ワークテーブル生成処理などによるローカルディスクへの物理書き込みに数十秒かかるケースがあります。
さらに、フロントエンドファイル(.accdb)の肥大化も引き起こします。
■ ネットワーク越しの一覧フォームが遅い
リンクテーブル経由の一覧フォームでは、Access特有の「細かく頻繁に通信する仕様(チャッティ)」が問題になります。
特にWi-FiやVPN環境では、
・スクロール時の引っかかり
・編集時の待ち時間
・画面フリーズ
が発生しやすくなります。
こうした「ディスクI/O」と「ネットワーク通信」の壁を突破するために辿り着いたのが、
「ディスクへ書き込まず、すべてメモリ上で処理する」
というアプローチです。
2. 極意その1:レコードソースを空にして「現物」をバインドする
通常、フォームの「レコードソース」にテーブルやクエリを設定すると、Accessは裏側でバックエンドへ接続し、自動的にデータ管理を行います。
しかし、この「Accessによる自動制御」こそが、速度低下や不要通信の原因になります。
そこで新手法では、フォームの「レコードソース」をあえて空欄にします。
その代わり、VBAからADO(ActiveX Data Objects)を使い、データをメモリ上へ一括取得し、そのRecordsetオブジェクト自体をフォームへ直接渡します。
<サンプルで使用するテーブルのイメージ>

コード例:ADOによる仮想バインド
' ADOでバックエンドファイルからデータを取得
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open _
"SELECT ID, ItemA, ItemB FROM T_SampleData", _
cn, _
adOpenStatic, _
adLockBatchOptimistic
' 【重要】
' バックエンドとの接続を完全に切断
Set rs.ActiveConnection = Nothing
cn.Close
' メモリ上のRecordsetをフォームへ直接バインド
Set Me.Recordset = rs
この方式では、
・ワークテーブル作成不要
・ディスク書き込みゼロ
・通信回数激減
となるため、数万件規模でも高速に表示できます。
スクロールも非常に滑らかで、ローカルデータを扱っているかのような操作感になります。
<フォームの実行結果のイメージ>

3. 極意その2:Accessのお節介な「自動保存」を封じる
Accessのフォームには、
行移動時に勝手に保存しようとする
という非常に強力な自動保存仕様があります。
通常、この挙動を完全制御するのはかなり困難です。
しかし今回の構成では、ADO Recordset を「切断状態」にしているため、この問題を根本的に回避できます。
ポイントはこの1行です。
Set rs.ActiveConnection = Nothing
この時点で、フォーム上のデータはバックエンドとの接続を失っています。
つまり、ユーザーが行移動しても、Accessは保存先へ通信できません。
結果として、
・意図しない自動保存を防止
・「保存ボタン押下時のみ更新」という制御が可能
・更新タイミングを100%開発者側で管理可能
になります。
これは、業務システムにおける安全性向上にも非常に効果的です。
4. 極意その3:ディクショナリで「変更履歴」を管理する
Recordset が切断状態である以上、ユーザーが編集した内容は、そのままでは保存されません。
そこで活用するのが、VBAの Scripting.Dictionary です。
このディクショナリに、
・更新データ
・新規データ
・削除対象
を蓄積し、「保存ボタン押下時」にまとめてDBへ反映します。
コード例:変更内容をディクショナリへ記録
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim dictKey As String
' ID未発番なら新規行
If IsNull(Me!ID.Value) Then
' 仮キーで管理
dictKey = "NEW_" & Me.CurrentRecord
dictChanges.Item(dictKey) = Array( _
Null, _
Me!ItemA.Value, _
Me!ItemB.Value, _
"SAVE" _
)
Else
' 既存データは実IDで管理
dictKey = CStr(Me!ID.Value)
dictChanges.Item(dictKey) = Array( _
Me!ID.Value, _
Me!ItemA.Value, _
Me!ItemB.Value, _
"SAVE" _
)
End If
End Sub
■ 新規行と既存行を正確に判定
IsNull(ID) を利用することで、
・未保存の新規行
・既存データの編集
を明確に区別できます。
新規追加後に再編集された場合でも、同じ仮キーへ上書きされるため、状態管理が崩れません。
■ 命令フラグで処理を切り分ける
配列の最後に、
・"SAVE"
・"DELETE"
などの命令フラグを保持することで、保存時に
・INSERT
・UPDATE
・DELETE
を正確に振り分けられます。
■ ディクショナリは「最終状態」だけを保持できる
Dictionaryは同じキーに対して自動上書きされるため、同一行を何度編集しても、最終状態だけが残ります。
つまり、
・差分管理がシンプル
・メモリ効率が良い
・更新処理を最適化できる
という大きなメリットがあります。
まとめ
「ADO切断」による超高速表示と、
「ディクショナリ」による堅牢な変更管理。
この2つを組み合わせることで、Access最大のボトルネックである
「ワークテーブルへの物理アクセス」
を完全に排除できます。
さらに、ユーザー操作中はデータベースと切断されており、保存時のみ瞬間的に接続するため、
・排他ロック競合の激減
・ファイル破損リスクの低下
・多人数同時利用時の安定性向上
といった副次効果も得られます。
大量データを扱うAccessシステムの高速化や、ファイル共有環境での安定運用を検討されている方は、ぜひ一度このアーキテクチャを試してみてください。
大手SIerに長年常駐し大規模システムの経験を重ねプログラムスキルとDB設計を磨く。AccessおよびExcelのVBAを得意とする。
⇒お仕事のご依頼はこちら


