ユーザーフォームでの履歴管理:変更ログを残すアプローチ

以下では、「ユーザーフォームでの履歴管理:変更ログを残すアプローチ」をテーマに、Access上で入力されたデータの変更履歴(いつ・誰が・何をどのように更新したか)を記録し、後から確認できるようにする方法を解説します。複数ユーザーが同じテーブルを編集するときや、監査ログが求められる業務シナリオで役立つアプローチです。
1. なぜ履歴管理(変更ログ)が必要か?
- 入力ミスや不正変更の追跡
– 業務上、どのタイミングで誰がどの項目を変更したかが分かると、間違いをすぐ訂正できたり、不正アクセスに早期気づけたりします。 - 監査要件対応
– コンプライアンスや監査上、更新履歴を一定期間保管しなければならないケースがある。 - 過去バージョンへのリストア
– 事故やトラブルで誤更新された場合、ある程度のロールバックが履歴から可能になる。
Accessは標準機能として変更履歴を自動保存する仕組みはありませんが、BeforeUpdate/AfterUpdateイベントやトリガーテーブルなどを工夫することで実現できます。
2. 代表的な履歴管理の手法
以下に、Accessでログを残す主な手法を紹介します。それぞれのやり方に長所・短所があります。
2.1 テーブル別にログテーブルを用意(レコード単位のスナップショット)
- tbl_顧客 の更新履歴を tbl_顧客ログ に記録するイメージ
- 各更新の前後で、レコード全体をコピーしたり、変更された列だけを記録
- 項目例:
– ログID (オートナンバー)
– 顧客ID (どの顧客が対象か)
– 更新日時 / 更新者ID
– 変更内容(旧値 / 新値) or レコードスナップショット
長所: 後から顧客IDや日時で検索しやすい
短所: 変更が頻繁だとログが膨大になる
2.2 全更新を1つの監査テーブルに集約(列名/旧値/新値)
- tbl_変更監査 を作り、すべてのテーブルの更新履歴をまとめて1つのテーブルに保存
- 項目例:
– 監査ID (オートナンバー)
– テーブル名 / フィールド名
– レコードID (主キー値)
– 旧値 / 新値
– 更新日時 / 更新者 - 更新対象が増えても、テーブルを新規作る必要はなく、統一フォーマットでログを集約できる
長所: テーブルの追加や変更があってもログテーブルは1つで済む
短所: フィールドが多いDBやリレーションが複雑な場合、解析に手間がかかる
3. フォームイベントを使った実装例
最も手軽な方法は、フォームのBeforeUpdateイベントにコードを仕込み、変更前/後の値を検知してログテーブルにINSERTするやり方です。
3.1 テーブル例:変更ログテーブル (tbl_変更ログ)
フィールド名 | データ型 | 説明 |
---|---|---|
ログID | オートナンバー | 主キー |
テーブル名 | 短いテキスト | 例: “tbl_顧客” |
レコードID | 数値など | 例: 変更対象の顧客ID, 商品IDなど |
フィールド名 | 短いテキスト | どのフィールドが変わったか |
旧値 (OldValue) | 短いテキスト | 更新前の値 |
新値 (NewValue) | 短いテキスト | 更新後の値 |
更新日時 | 日付/時刻型 | Now() などで更新時刻を記録 |
更新者 | 短いテキスト | Windowsログイン名やユーザーIDなど |
3.2 BeforeUpdateイベントで差分を判定
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim fld As DAO.Field
Dim db As DAO.Database
Dim strSQL As String
Dim oldVal As String, newVal As String
Set db = CurrentDb
' 顧客IDなど主キー値を取得 (例: 主キーが 顧客ID だとする)
Dim lngID As Long
lngID = Me.顧客ID ' フォーム上の主キー
' テーブル名は固定か、フォームのRecordSourceを参照するなど
Dim strTable As String
strTable = "tbl_顧客"
' フォームで編集された各フィールドを巡回
For Each fld In Me.Recordset.Fields
' フィールド単位で OldValue と Value(=NewValue) の差分をチェック
If fld.Name <> "顧客ID" Then ' 主キーは除外
If Nz(Me(fld.Name).OldValue, "") <> Nz(Me(fld.Name).Value, "") Then
oldVal = Nz(Me(fld.Name).OldValue, "")
newVal = Nz(Me(fld.Name).Value, "")
' ログテーブルにINSERT
strSQL = "INSERT INTO tbl_変更ログ " & _
"(テーブル名, レコードID, フィールド名, 旧値, 新値, 更新日時, 更新者)" & _
" VALUES(" & _
"'" & strTable & "', " & lngID & ", " & _
"'" & fld.Name & "', " & _
"'" & Replace(oldVal, "'", "''") & "', " & _
"'" & Replace(newVal, "'", "''") & "', " & _
"'" & Format(Now(), "yyyy-mm-dd hh:nn:ss") & "', " & _
"'" & Environ("Username") & "');"
db.Execute strSQL, dbFailOnError
End If
End If
Next fld
Set db = Nothing
End Sub
ポイント解説:
Me.Recordset.Fields
でフォームバインドされている各フィールドを回すOldValue
とValue
(またはNewValue
) を比較し、違いがあればログテーブルにInsertReplace(oldVal, "'", "''")
でシングルクォートのエスケープ(SQLインジェクション対策)Environ("Username")
でWindowsユーザー名を取得し、更新者として記録(他の方法でユーザーID管理する場合も)
このようにBeforeUpdateイベントで書くと、レコード単位の更新時点でログが残り、未変更のフィールドはログを追加しないように管理できます。
4. 運用とデメリットを意識しよう
- ログが肥大化
- 頻繁に更新されるフィールドがあると、ログテーブルが爆発的に増える可能性あり
- 定期的なアーカイブや上限管理が必要
- すべてのフォームを対象にしないと漏れる
- テーブルへ直接クエリでUPDATEしているケースや別フォーム更新などはカバーしにくい
- 全更新を対象にしたい場合はトリガー(SQL Server) を使うか、あらゆる経路でログ書き込みを徹底する
- 参照整合性
- ログテーブル内の
レコードID
が削除された本体レコードと整合しなくなる - レコード削除時にもログを残すかどうか検討
- ログテーブル内の
- パフォーマンス
- 更新のたびに
INSERT
が走るため、大量データを高速更新する場合にはオーバーヘッドになる - DBのメンテナンスや適切なインデックス管理を行う
- 更新のたびに
5. 全クエリ・テーブル操作に対応するには?
BeforeUpdateイベントはフォームを介した更新に強いですが、クエリ操作や他モジュールなどから直接更新が行われる場合はログが取れません。この場合、以下のアプローチが考えられます:
- すべての更新をフォーム経由に限定
- ポリシーとして「クエリやテーブル直接編集は禁止」とし、ユーザーが更新時は必ずフォームを介させる
- SQL Serverのトリガーを使う(バックエンドがSQL Serverの場合)
- テーブルに
AFTER INSERT/UPDATE/DELETE
トリガーを仕込み、ログテーブルへ書き込み - AccessでなくDBサーバー側で集中管理できる
- テーブルに
- Accessクエリを拡張
- アクションクエリ(UPDATE/DELETE)をマクロ/VBAでラップし、実行前後でログを記録する
6. ログの活用:検索・ロールバック】
- ログ閲覧フォーム
- tbl_変更ログを元に「いつ/誰が/何を変更したか」を一覧表示
- 顧客IDや日付でフィルタリングできるようにしておくと便利
- 差分リストア(半手動)
- もし誤更新が判明したら、ログに残る旧値を確認し、手動で戻す
- プログラム的に一括ロールバックする仕組みは複雑なので、通常はログから該当変更を調べて修正するケースが多い
7. まとめ:フォームでの履歴管理が現場での安心感を高める
- フォームイベント + ログテーブルで「誰が、いつ、どの列を、どう変えたか」を記録できる
- 単一フォームだけなら実装はそこまで難しくないが、複数の更新経路があるなら統一的なログ仕組みを整え、運用にブレが生じないよう配慮
- ログテーブル管理やストレージ肥大化、メンテナンスを視野に入れて長期運用を計画する
エンドユーザーから「誰がいつ変更したか分かるようにして!」という要望があった際には、上記の手法を応用すればスムーズに対応可能です。初めはフォームのBeforeUpdateで簡易的に差分を取るところから始め、必要に応じてトリガーやその他高度な仕組みに発展させていきましょう。
関連記事
今回紹介した変更ログのアプローチを導入すれば、Accessでも監査ログやトレーサビリティを確保でき、信頼性の高いシステム運用が可能になります。ぜひ試してみてください。