AccessVBAでSQLを何度も発行する。
AccessVBAを利用していると処理中に何度もSQLを発行したい場合があります。
実績集計処理
一括でデータを集計する処理
対象データ抽出SELECT
実績集計INSERT
集計結果UPDATE
前月データの削除DELETE
処理結果ログINSERT
画面操作に連動したデータ更新
フォーム操作に応じて、複数テーブルを順に更新するパターン
ボタン押下で主テーブルUPDATE
明細テーブルINSERT
集計テーブルUPDATE
ログテーブルINSERT
データチェック・整合性検証
業務ルールに基づいて、条件チェックを段階的に行うケースです。
件数チェック用SELECT
重複確認用SELECT
不整合データ抽出SELECT
特にSELECT文を発行する場合には、処理の度に以下文を書かなければなりません。
Dim db As DAO.Database
Set db = CurrentDb上記記載を軽減し、SELECTではRecordsetを返しINSERT,UPDATE,DELETEではSQLのみ実行するプロシージャを紹介します。
SQLを実行するプロシージャサンプル
サンプルコードを紹介します。
'---------------------------------------------------------
' プロシージャ名:gf_EXEC_SQL
' 機能 :Access でSQL文を実行します。
'
' 【引数】
' strSqlStatement :実行するSQL文
' [objRecordset] :SELECT文の結果を受け取るRecordset(省略可能)
'
' 【戻り値】
' True … 正常終了
' False … エラー発生
'
' 【処理概要】
' ・Recordset指定が無い場合 → Executeで更新系SQL実行
' ・Recordset指定がある場合 → OpenRecordsetで結果セットを返す
'
' 【使用例】
' '--- SELECT文(結果をRecordsetで取得)---
' Dim rs As DAO.Recordset
'
' Call gf_EXEC_SQL("SELECT * FROM T_社員", rs)
'
' '--- 更新系SQL(INSERT/UPDATE/DELETEなど)---
' Call gf_EXEC_SQL("DELETE FROM T_社員 WHERE ID=1")
'---------------------------------------------------------
Public Function gf_EXEC_SQL(strSqlStatement As String, Optional objRecordset)
On Error GoTo err_Exec_SQL
Dim l_db As DAO.Database
'=== 現在のDBを取得 ===
Set l_db = CurrentDb()
'=== Recordset引数の有無で処理分岐 ===
If IsMissing(objRecordset) Then
'--- 更新系SQLを実行(結果は返さない)---
l_db.Execute strSqlStatement
Else
'--- SELECT文を実行し結果セットを返す ---
Set objRecordset = l_db.OpenRecordset(strSqlStatement, DB_OPEN_SNAPSHOT)
End If
gf_EXEC_SQL = True
Exit Function
err_Exec_SQL:
'=== エラー処理 ===
MsgBox "【gf_EXEC_SQL】SQL実行中にエラー発生" & vbCrLf & _
"番号:" & Err.Number & vbCrLf & _
"内容:" & Err.Description, vbCritical
Debug.Print "[SQL Error] " & strSqlStatement
gf_EXEC_SQL = False
End Function
SELECT文の場合にIsMissing(objRecordset) で判定し、結果セットを返すという単純な処理ですが、一つのプロシージャで済むので生産性が向上します。
実行方法
SELECT文の場合には以下のように利用します。
Dim rs As DAO.Recordset
Call gf_EXEC_SQL("SELECT * FROM T_社員", rs)引数にSQLとRecordsetをそれぞれ渡すと処理が可能となっています。
INSERT,UPDATE,DELETEの場合は以下のように利用します。
Call gf_EXEC_SQL("DELETE FROM T_社員 WHERE ID=1")引数にSQLを渡すと処理が可能となっています。
トランザクション処理を行いたい。
複数SQLのBeginTrans、CommitTrans、エラー時にロールバックする関数も書いてみました。
'================================================================================
' プロシージャ名:gf_TRAN_SQL
' 機能 :複数SQLをトランザクションで実行します。
'
' 【引数】
' arrSqlStatement :SQL文配列(Variant配列)
'
' 【戻り値】
' True … Commit成功
' False … エラー時Rollback
'================================================================================
Public Function gf_TRAN_SQL(arrSqlStatement As Variant) As Boolean
On Error GoTo err_TRAN_SQL
Dim l_ws As DAO.Workspace ' Workspace(トランザクション管理)
Dim l_db As DAO.Database ' Database
Dim l_lp As Long ' ループカウンタ
Dim l_sql As String ' 実行SQL
'=== オブジェクト取得 ===
Set l_ws = DBEngine.Workspaces(0)
Set l_db = CurrentDb
'=== トランザクション開始 ===
l_ws.BeginTrans
'=== SQL実行 ===
For l_lp = LBound(arrSqlStatement) To UBound(arrSqlStatement)
l_sql = Nz(arrSqlStatement(l_lp), "")
If l_sql <> "" Then
l_db.Execute l_sql, dbFailOnError
End If
Next l_lp
'=== Commit ===
l_ws.CommitTrans
gf_TRAN_SQL = True
Exit Function
err_TRAN_SQL:
'=== Rollback ===
On Error Resume Next
l_ws.Rollback
On Error GoTo 0
MsgBox "【gf_TRAN_SQL】トランザクションエラー" & vbCrLf & _
"Index : " & l_lp & vbCrLf & _
"SQL : " & l_sql, vbCritical
Debug.Print "Index : " & l_lp
Debug.Print "SQL : " & l_sql
gf_TRAN_SQL = False
End Functionトランザクション処理なので、配列を渡してあげると複数SQLを流して処理が可能となっています。
実行方法はこのような形です。
Private Sub sample_TRAN_SQL()
Dim arrSql() As String
ReDim arrSql(2)
arrSql(0) = "DELETE FROM T_受注 WHERE 受注日 < #2024-01-01#"
arrSql(1) = "INSERT INTO T_ログ (内容) VALUES ('受注削除実行')"
arrSql(2) = "" ' 空文字は自動スキップされる
If gf_TRAN_SQL(arrSql) Then
MsgBox "処理完了"
Else
MsgBox "エラー"
End If
End Sub引数に配列形式でSQLを渡すと処理が可能となっています。
最後に
以上がAccessVBAでSQLを何度も発行を簡略化する方法です。
Access VBAで実績集計やバッチ処理を実装していると、SQLを何度も発行する場面は避けて通れません。
そのたびに Execute や OpenRecordset を個別に書いていると、処理の流れが見えにくくなり、修正や障害対応のコストも確実に増えていきます。
SQL実行処理を共通関数として切り出すことで、業務ロジックとDB操作を明確に分離でき、コードの可読性・保守性は大きく向上します。


コメント