【実務・中級編】SQLの「UNION ALL」を動的に生成する:複数のテーブルからデータを統合する汎用関数 – Access VBA解析バイブル

スポンサーリンク

Access VBAで「UNION ALL」を動的に制する:極限のクエリ構築術

業務システム開発において、Accessの「UNION ALL」は諸刃の剣だ。複数のテーブルからデータを統合する際、安易にクエリをハードコーディングしていないか? 「テーブルが増えるたびにクエリを書き換える」ような設計は、技術的負債以外の何物でもない。

今回は、メンテナンス性を極限まで高めた「動的UNION ALL生成エンジン」を構築する知見を伝授する。

—

1. なぜ「静的クエリ」ではいけないのか

多くの開発者は、クエリデザイナで作成したSQLをそのままVBAに埋め込む。だが、以下のリスクを考慮しているか?

  • 変更への脆弱性: テーブル構造や増設に対応できず、改修工数が肥大化する。
  • デバッグの困難さ: 実行時の動的な条件付与が文字列連結で行われ、構文エラーの温床となる。
  • パフォーマンスのブラックボックス化: 実行プランが最適化されないまま実行されるリスク。

我々が目指すべきは、「構造を抽象化し、実行時に堅牢なSQLを組み立てる」アーキテクチャだ。

—

2. 堅牢なSQL生成エンジンの設計指針

今回作成する関数 `GenerateUnionSQL` は、以下の設計原則に基づいている。

1. 配列引数による拡張性: `Variant`型の配列でテーブル名を渡すことで、テーブルの増減に即座に対応する。
2. パラメータクエリの活用: SQLインジェクションを防ぎ、実行プランのキャッシュ効率を高める(DAO.QueryDefの活用)。
3. エラーハンドリングの徹底: 生成されたSQLが妥当か、事前検証を行う。

—

3. 実装コード:汎用動的SQL生成モジュール

以下のコードを標準モジュールに配置し、即戦力として活用してほしい。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 機能:指定されたテーブル配列をUNION ALLで結合するSQLを生成する
‘ 引数:arrTables – テーブル名の配列
‘ strWhere – 共通の抽出条件(WHERE句 ※WHEREを含めず記述)
‘ 戻り値:生成されたSQL文字列
‘ ==============================================================================
Public Function GenerateUnionSQL(arrTables As Variant, Optional strWhere As String = “”) As String
Dim i As Long
Dim strSQL As String
Dim strResult As String

‘ 配列の妥当性チェック
If Not IsArray(arrTables) Then Exit Function

For i = LBound(arrTables) To UBound(arrTables)
‘ 各テーブルから必要なカラムを取得(ではなく明示的に指定することを推奨)
strSQL = “SELECT FROM [” & arrTables(i) & “]”

‘ 条件付与
If Len(strWhere) > 0 Then
strSQL = strSQL & ” WHERE ” & strWhere
End If

strResult = strResult & strSQL

‘ 最後の要素以外にUNION ALLを付加
If i < UBound(arrTables) Then strResult = strResult & " UNION ALL " End If Next i GenerateUnionSQL = strResult End Function ' --- 実行例:フォームやレポートのレコードソースを動的に切り替える場合 --- Public Sub ApplyDynamicSource(targetForm As Form, tableList As Variant) Dim sql As String sql = GenerateUnionSQL(tableList, "Status = 'Active'") ' レコードソースの書き換え targetForm.RecordSource = sql End Sub ---

4. アーキテクトからの重要な忠告

この手法を運用する上で、必ず守るべき「3つの鉄則」がある。

① `SELECT ` の禁止

上記の例では汎用性を重視して “ を使用したが、本番環境では「必要なカラムのみを明示的に記述」すること。テーブルの列順が変わっただけでツールが崩壊するのを防ぐためだ。

② `QueryDef` での実行を優先せよ

単なる文字列としてSQLを扱うのではなく、`CurrentDb.CreateQueryDef` を使用して一時的にクエリを作成し、それを実行・参照する構成にすれば、Accessのクエリ最適化エンジンが正しく機能する。

③ 実行計画のキャッシュを意識する

Access(JET/ACEエンジン)はSQL文字列が完全一致した場合にクエリプランをキャッシュする。動的な条件(WHERE句)を生成する際は、可能な限りパラメータクエリ(`[Forms]![frmMain]![txtDate]` 等)をSQL内に埋め込み、文字列そのものを変化させない工夫が重要だ。

—

最後に:自動化の先にあるもの

このコードは単なる「文字列連結」ではない。テーブルという「データコンテナ」を抽象化し、VBAという「制御層」から分離するための第一歩だ。

コードを書くとき、常に自問自答してほしい。「これは半年後の自分が、何のドキュメントも見ずに修正できるコードか?」と。もし答えが「No」なら、それは負債である。

現場で困ったことがあれば、またいつでも聞きに来てほしい。エンジニアの戦いは、泥臭い実装の積み重ねの先にあるのだから。

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