【テクニカル・上級編】SQL Serverの「パススルー」クエリをVBAで制御:接続文字列の動的切り替えと認証情報の安全な管理 – Access VBA解析バイブル

スポンサーリンク

Access VBAを掌握する極限の知見:SQL Serverパススルークエリの動的制御とセキュア接続アーキテクチャ

リンクテーブルという幻想を捨てよ。
大規模なデータセットを前に、AccessのODBCレイヤーが吐き出す非効率なクエリ変換に頭を悩ませる時代は終わった。真にスケーラブルなAccessアプリケーションを構築する者にとって、SQL Serverの能力を限界まで引き出す唯一の解法は「パススルークエリ(Pass-Through Query)」の動的制御に他ならない。

今回は、VBAの`QueryDef`オブジェクトを駆使し、実行時に接続文字列や認証情報を安全に切り替えてSQL Serverへ直接クエリを投げる極限のテクニックを授ける。

—

1. パスルークエリの本質とリンクテーブルの決別

リンクテーブルは、Accessのローカルエンジン(JET / ACE)にSQLの構文解析や最適化を行わせるため、複雑な結合や集計においてネットワーク帯域とクライアント側のメモリを無駄に消費する。

一方、パススルークエリは、Accessはただの「郵便受け」に徹し、発行されたSQL文字列を一切の改変なしにSQL Serverへ丸投げする。処理はすべてサーバー側で完結し、結果セットだけが返される。
このアーキテクチャにおいて最も重大な課題となるのが、「ハードコーディングされた接続文字列(平文のパスワード含む)」の排除と、環境(本番・検証)に応じた動的な接続先切り替えである。

—

2. セキュアな接続管理と認証情報の分離

レガシーなVBAコードによく見られるのが、パスワードを含む接続文字列をそのままソースコードやクエリ定義プロパティに直書きする愚行である。これはセキュリティ監査において一発レッドカードの脆弱性となる。

シニアエンジニアたる者、接続文字列は以下の方針で管理しなければならない。
1. Windows認証(統合セキュリティ)の優先:可能な限りパスワードをコード内に持たない。
2. ODBC接続文字列の動的アセンブル:接続先(Server, Database)のみを動的に書き換え、QueryDefの構造体にインメモリで流し込む。
3. 明示的なオブジェクト解放(メモリ最適化):ADO/DAOオブジェクトのライフサイクルを完全に制御し、Access特有の肥大化を防ぐ。

—

3. 実装コード:動的パススルークエリ実行エンジン

以下に、接続文字列を動的に構築・適用し、SQL Serverへ安全にパススルークエリを発行する実用的なVBAモジュールを提示する。エラーハンドリングとオブジェクトのクリーンアップを極限まで突き詰めた実装だ。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 担当: チーフアーキテクト
‘ 概要: SQL Serverパススルークエリを動的に生成・実行し、セキュアに破棄する
‘ ==============================================================================

Public Sub ExecuteSecurePassThrough(ByVal pstrQueryName As String, _
ByVal pstrSQL As String, _
ByVal pblnReturnRecords As Boolean)

Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strConn As String

‘ 1. 接続文字列の構築(環境変数や安全な設定テーブルから取得することを推奨)
‘ ※ここではODBCドライバ「ODBC Driver 17 for SQL Server」を使用する前提
strConn = BuildConnectionString( _
ServerName:=”sql-server-prod.internal.local”, _
DatabaseName:=”EnterpriseDB”, _
UseIntegratedSecurity:=True, _
UserId:=””, _
Password:=”” _
)

Set db = CurrentDb()

On Error GoTo ErrorHandler

‘ 2. 既存のクエリ定義が存在する場合は削除(名前衝突の回避)
For Each qdf In db.QueryDefs
If qdf.Name = pstrQueryName Then
db.QueryDefs.Delete pstrQueryName
Exit For
End If
Next qdf

‘ 3. 新規にQueryDef(パススルークエリ)を動的生成
Set qdf = db.CreateQueryDef(pstrQueryName)

‘ 4. プロパティの設定(超重要:ODBCDirectではなくDAOのパススルー設定)
With qdf
.Connect = strConn
.SQL = pstrSQL
.ReturnsRecords = pblnReturnRecords
‘ タイムアウト設定(秒単位: デフォルトは60秒。必要に応じて延長)
.ODBCTimeout = 300
End With

‘ 5. 実行
If pblnReturnRecords Then
‘ レコードを返す場合(SELECT等)はレコードセット等で受けるか、フォームのソースにする
Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset(dbOpenSnapshot)

‘ — ここでデータを処理 —
MsgBox “取得レコード数: ” & rs.RecordCount, vbInformation, “パススルー実行成功”

rs.Close
Set rs = Nothing
Else
‘ レコードを返さない場合(UPDATE, INSERT, 存命ストアド等)
qdf.Execute dbFailOnError
End If

CleanUp:
‘ 6. オブジェクトの明示的解放(メモリリークの完全防止)
If Not qdf Is Nothing Then
‘ 一時的なクエリとして残すか、使い捨てて削除するかを用途で決める
‘ ここでは使い捨て(削除)にする場合
db.QueryDefs.Delete pstrQueryName
End If

Set qdf = Nothing
If Not db Is Nothing Then
db.Close
Set db = Nothing
End If
Exit Sub

ErrorHandler:
MsgBox “パススルークエリの実行に失敗しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub

‘ ——————————————————————————
‘ 接続文字列ビルダー(セキュアアセンブリ)
‘ ——————————————————————————
Private Function BuildConnectionString(ByVal ServerName As String, _
ByVal DatabaseName As String, _
ByVal UseIntegratedSecurity As Boolean, _
ByVal UserId As String, _
ByVal Password As String) As String

Dim str As String

‘ 最新のODBCドライバを指定
str = “ODBC;DRIVER={ODBC Driver 17 for SQL Server};” & _
“SERVER=” & ServerName & “;” & _
“DATABASE=” & DatabaseName & “;”

If UseIntegratedSecurity Then
str = str & “Trusted_Connection=Yes;”
Else
‘ 混雑したネットワーク環境等でSQL認証を使う場合のフォールバック(極力避けること)
str = str & “UID=” & UserId & “;PWD=” & Password & “;”
End If

‘ 接続の信頼性を高めるオプション
str = str & “Encrypt=Yes;TrustServerCertificate=Yes;”

BuildConnectionString = str
End Function

—

4. チーフアーキテクトからの実践的警鐘:メモリとライフサイクル

Access VBAの最大の闇は、「ガベージコレクションの曖昧さ」と「システムカタログ(MSysObjects)の肥大化」にある。

前述のコードでは、一時的な処理のために `db.CreateQueryDef` でクエリを動的生成し、処理の直後に `db.QueryDefs.Delete` で破棄している。
これを怠り、Accessのコンテナ内に数千ものパススルークエリを残留させると、Accessの内部データベースファイル(ACCDB)は瞬く間に断片化し、パフォーマンスは地に落ち、最終的には「データベースが破損しています」という破滅的な結末を迎えることになる。

もし「毎回クエリを作っては消す」オーバーヘッドを嫌うのであれば、あらかじめアプリケーション内に空のパススルークエリを1つ用意(例: `ptQuery_Template`)しておき、VBA側でその `.Connect` と `.SQL` プロパティだけを毎回書き換えて実行するという設計手法をとれ。これならばシステムカタログを汚染せず、極限のパフォーマンスを発揮する。

結論

Accessを単なる「ローカルおもちゃデータベース」として扱う時代は終わった。
SQL Serverの堅牢性とスケーラビリティをバックボーンに据え、VBAのコードからパススルークエリの接続を自在にコントロールする――この境地に到達したシステムだけが、レガシーの呪縛を断ち切り、モダンな基幹系クライアントとして半永久的に稼働し続けることができる。

コードを書け。そしてオブジェクトを解放せよ。アーキテクチャの美しさは、その厳格な後始末に宿る。

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