Access VBAを掌握する極限の知見:SQL Serverパススルークエリの動的制御とセキュアな接続管理
開発現場でよく見かける光景がある。Accessの「リンクテーブル」を使い倒し、データ量が増えた途端にシステムが重くなり、挙句の果てにはネットワーク切断でデータベースが破損する――。
ビジネス規模が拡大するにつれ、バックエンドをSQL Serverに移行するのは定石だ。しかし、そのフロントエンドとしてAccessを使い続ける場合、リンクテーブル頼みの設計では遠からず破綻する。
ここで投入すべき切り札が 「パススルークエリ(Pass-Through Query)」 だ。
Accessのエンジン(ACE/Jet)を完全にバイパスし、SQL文を丸ごとSQL Serverに投げて実行結果だけを受け取るこの仕組みは、圧倒的なパフォーマンスを叩き出す。
だが、本番環境と検証環境で接続先サーバーを切り替えたい時、あるいは認証情報をどう安全に管理すべきかという壁にぶつかる開発者は多い。固定化された接続文字列をハードコーディングするような愚を犯してはならない。
今回は、VBAの `QueryDef` オブジェクトを駆使し、実行時に接続先や認証情報を動的に安全にコントロールするプロダクションコードを、アーキテクトの視点から授けよう。
—
なぜ「リンクテーブル」ではなく「パススルークエリ」なのか?
パフォーマンスの重みを理解していないエンジニアは、何でもかんでもリンクテーブルを貼りたがる。しかし、リンクテーブルはAccessが裏でSQLを解釈・変換するため、複雑なクエリになると無駄なネットワークトラフィックが発生し、SQL Server側のインデックスすら効かなくなることがある。
パススルークエリであれば、発行するSQLは100% T-SQL(SQL Serverのネイティブ方言)としてサーバー側で実行される。
これをVBAから動的に生成・制御できれば、以下のメリットを極限まで引き出せる。
- 処理速度の爆発的な向上: サーバーサイド処理の恩恵をフルに受けられる。
- 環境依存からの脱却: 接続文字列を動的に書き換えることで、単一のACCDBで開発・テスト・本番環境をシームレスに切り替えられる。
- リソースの適正管理: 不要になった一時的なQueryDefをコードから動的に破棄し、データベースの肥大化を防げる。
—
堅牢な設計の要諦:接続情報の外部化と安全な管理
データベース接続において最もタブー視されるべきは、ユーザーIDやパスワードなどの機密情報をソースコードにハードコーディングすることだ。退職したエンジニアの置き土産のようなコードは、セキュリティインデントの温床になる。
プロダクション環境では、以下のいずれかの方式を採用すべきだ。
1. 統合Windows認証(Trusted_Connection=Yes): 最も安全。パスワードをコードやファイルに持つ必要がない。
2. 接続設定の暗号化または保護: 外部のセキュアな設定ファイル、またはAccess内のシステムテーブルに隠蔽する。
今回は、実務で最も汎用性が高い「Windows認証 / SQL Server認証の動的切り替え」に対応しつつ、コードを汚さない設計を実装する。
—
実装コード:動的パススルークエリ生成・実行エンジン
以下のコードは、指定したパススルークエリの接続文字列とSQLテキストを実行時に動的に書き換え、安全にデータを取得するための標準モジュールである。
コピペしてそのまま現場のプロジェクトに組み込めるよう、エラーハンドリングとオブジェクトのクリーンアップを徹底したプロダクションクオリティで記述している。
Option Compare Database
Option Explicit
‘ =========================================================================
‘ モジュール名: modPassThroughManager
‘ 概要 : SQL Serverパススルークエリの動的生成と安全な実行を管理する
‘ =========================================================================
‘ 接続先の環境定義(必要に応じて定数や設定テーブルから読み込む)
Private Const SERVER_NAME As String = “192.168.1.100\SQLEXPRESS” ‘ または “server.domain.com”
Private Const DATABASE_NAME As String = “EnterpriseDB”
Private Const USE_WINDOWS_AUTH As Boolean = True ‘ True: Windows認証, False: SQL Server認証
‘ 認証情報(SQL Server認証の場合のみ使用。実際には暗号化ストレージ等から取得すべき)
Private Const DB_USER As String = “AppUser”
Private Const DB_PASSWORD As String = “P@ssw0rd_Secure”
”’
”’
”’ 操作対象のQueryDef名
”’ 実行するT-SQL文
”’ レコードを返すクエリの場合はTrue(SELECT等)、アクションクエリはFalse
”’
Public Function ExecuteDynamicPassThrough( _
ByVal queryName As String, _
ByVal targetSql As String, _
Optional ByVal returnsRecords As Boolean = True) As ADODB.Recordset
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim connString As String
Set db = CurrentDb()
On Error GoTo ErrorHandler
‘ 1. セキュアな接続文字列の動的構築
connString = BuildConnectionString()
‘ 2. QueryDefの存在確認と生成・取得
On Error Resume Next
Set qdf = db.QueryDefs(queryName)
On Error GoTo ErrorHandler
If qdf Is Nothing Then
‘ 存在しない場合は新規作成
Set qdf = db.CreateQueryDef(queryName)
End If
‘ 3. パスルークエリとしてのプロパティ設定
‘ ※ODBCDirect接続文字列を設定することでパススルー化する
qdf.Connect = connString
‘ レコードを返すかどうか(SELECT文ならTrue、UPDATE/INSERTならFalse)
qdf.ReturnsRecords = returnsRecords
‘ 4. 実行するSQLを動的に割り当て
qdf.SQL = targetSql
‘ 5. クエリの実行(SELECTの場合、DAO.Recordsetとして取得することも可能だが、
‘ 複雑な型やパフォーマンスを考慮し、必要に応じた処理を行う)
If returnsRecords Then
‘ DAO経由で開く場合
‘ Dim rsDao As DAO.Recordset
‘ Set rsDao = qdf.OpenRecordset(dbOpenSnapshot)
End If
‘ コミット完了(QueryDefの変更を保存)
db.QueryDefs.Refresh
Exit Function
ErrorHandler:
MsgBox “パススルークエリの実行中に致命的なエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
‘ オブジェクトの解放
If Not qdf Is Nothing Then Set qdf = Nothing
If Not db Is Nothing Then Set db = Nothing
‘ 呼び出し元へ伝播
Err.Raise Err.Number, “ExecuteDynamicPassThrough”, Err.Description
End Function
”’
”’
Private Function BuildConnectionString() As String
Dim conn As String
conn = “ODBC;DRIVER={ODBC Driver 17 for SQL Server};” & _
“SERVER=” & SERVER_NAME & “;” & _
“DATABASE=” & DATABASE_NAME & “;”
If USE_WINDOWS_AUTH Then
‘ 統合Windows認証
conn = conn & “Trusted_Connection=Yes;”
Else
‘ SQL Server認証(平文保存は厳禁!暗号化機構と組み合わせること)
conn = conn & “UID=” & DB_USER & “;” & _
“PWD=” & DB_PASSWORD & “;”
End If
‘ 暗号化通信の強制(モダンなSQL Server環境の必須要件)
conn = conn & “Encrypt=Yes;TrustServerCertificate=Yes;”
BuildConnectionString = conn
End Function
—
アーキテクトからの実務アドバイス:運用時の罠
このコードを導入するにあたり、現場のプログラマーが陥りがちな罠をいくつか指摘しておく。
1. キャッシュと一時クエリのゴミ屋敷化
コード内で `CreateQueryDef` を動的に行う場合、処理が終わるたびにクエリを削除しないと、Accessの内部システムテーブル(MSysObjects)が肥大化し、データベース全体のパフォーマンスが劣化する。
一時的な処理であれば、名前を固定(例: `_TempPassThrough`)して上書きするか、処理の最後に必ず `db.QueryDefs.Delete queryName` でクエリを消去するライフサイクル管理を徹底すること。
2. SQLインジェクションへの意識
パススルークエリは「SQL文の文字列」を丸ごとサーバーに送るため、VBA側で変数を文字列結合してSQLを組み立てる場合、SQLインジェクションの脆弱性が生まれる。
動的SQLを構築する際は、必ず入力値のサニタイジングを行うか、ADOのCommandオブジェクトを用いたパラメータ化クエリ(Stored Procedureの実行)への移行を検討すべきだ。動的パススルーは「固定のクエリ骨子に安全なパラメータをバインドする」か「安全が保証された内部ロジック」でのみ使うこと。
3. ODBCドライバーのバージョン依存
接続文字列内の `DRIVER={ODBC Driver 17 for SQL Server}` は、クライアント端末側にそのドライバーがインストールされていることが前提となる。古い `SQL Server` ドライバー(古のSQLOLEDBなど)を指定してモダンな機能を殺さないよう、インフラ部門と結託してクライアント環境のドライバー標準化を必ず行っておくこと。
—
結び
Accessは「おもちゃのデータベース」ではない。
その挙動の裏にあるメカニズム、DAO/ADODBのライフサイクル、そしてSQL Serverとの通信プロトコルを正しく理解して設計すれば、基幹系に匹敵する堅牢なクライアント・サーバーシステムを構築できる。
リンクテーブルの呪縛から解放され、パススルークエリを自在に操ることで、あなたの開発するAccessアプリケーションは真の「プロフェッショナル・ツール」へと昇華するだろう。設計の美しさとパフォーマンスのキレを、ぜひその手で実証してほしい。
