【テクニカル・上級編】【中級】フィールドの「Unicode圧縮」プロパティをVBAで一括制御し、DBサイズを最適化する – Access VBA解析バイブル

スポンサーリンク

Access VBAを掌握する極限の知見:Unicode圧縮の支配者

データベースの肥大化に怯える日々は、今日で終わりにしよう。
Accessが2GBの壁に直面し、インデックスがフラグメントを起こし、ネットワーク越しに転送されるMDB/ACCDBファイルが重荷になっていく。その根本原因の一つが、デフォルトで放置された「テキスト型・メモ型フィールドの肥大化」にある。

シニアアーキテクトであれば知っているはずだ。Accessのテキストデータ(`Text`型)は、内部的にUTF-16(2バイト/文字)で保持される。しかし、日本の業務システムで扱われる大半の文字列――JIS第1・第2水準の漢字、英数カナ――は、上位バイトが常に `0x00` である。この「無駄なゼロバイト」を削ぎ落とし、DBサイズを極限まで圧縮するのが `UnicodeCompression` プロパティ だ。

今回は、DAO(Data Access Objects)を駆使して、数千あるフィールドの同プロパティをVBAで一括制御し、システム全体のフットプリントを最適化する極限の知見を授ける。

—

1. なぜ「Unicode圧縮」の制御が必要なのか?

AccessのUI上では、テーブルのデザインビューで各フィールドの「Unicode 圧縮」を「はい/いいえ」で切り替えられる。しかし、これを手作業で何百というテーブル、何千というフィールドに対して行うのは、エンジニアの仕事ではない。

アーキテククトが知るべきプロパティの裏側

  • 対象データ型: `dbText`(テキスト型)のみ。`dbMemo`(長文テキスト型)には適用できない(メモ型は独自の圧縮アルゴリズムを持つか、別領域に格納される)。
  • トレードオフ: 圧縮・解凍のわずかなCPUオーバーヘッドが発生する。しかし、現代のPCスペックにおいて、I/O(ディスク読み書き・ネットワーク転送)のボトルネックと比較すれば、CPU負荷など誤差に過ぎない。むしろ、キャッシュヒット率が向上する分、トータルのパフォーマンスは跳ね上がる。

—

2. 実装:DAOによるプロパティ一括制御スクリプト

ここに示すのは、カレントデータベース内の全テーブルを走査し、すべてのテキスト型フィールドの `UnicodeCompression` プロパティを強制的に `True`(はい)に書き換えるプロシージャだ。

オブジェクトの解放(ライフサイクルの管理)、エラーハンドリング、そしてADOではなくDAOをあえて選択する理由(DDL/DMLの速度とテーブル定義への直接アクセス)がコードの隅々に宿っている。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ 務自動化アーキテクチャ基盤: Unicode圧縮 一括適用エンジン
‘ =========================================================================
Public Sub OptimizeUnicodeCompression()
Dim dbs As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim lngModifiedCount As Long
Dim lngTableCount As Long

‘ 処理時間の計測開始
Dim dblStartTime As Double
dblStartTime = Timer

On Error GoTo ErrorHandler

Set dbs = CurrentDb
lngModifiedCount = 0
lngTableCount = 0

Debug.Print “=== Unicode圧縮 最適化プロセスを開始します ===”

‘ トランザクションはDDL(TableDefの変更)には効かないため、
‘ エラー時のロールバックはバックアップファイル(.accdb)の事前取得で担保すること。

For Each tdf In dbs.TableDefs
‘ システムテーブル(~で始まるものやMSys)はスキップする
If (tdf.Attributes & dbSystemObject) = 0 And Left$(tdf.Name, 4) <> “MSys” Then
lngTableCount = lngTableCount + 1

For Each fld In tdf.Fields
‘ テキスト型(dbText)のみを対象とする
‘ ※dbMemo型はUnicodeCompressionプロパティを持たないため除外
If fld.Type = dbText Then

‘ プロパティが存在するか確認しつつ設定を試みる
If SetUnicodeCompressionSafe(fld, True) Then
lngModifiedCount = lngModifiedCount + 1
End If

End If
Next fld
End If
Next tdf

Debug.Print “=== 最完遂完了 ===”
Debug.Print “処理対象テーブル数: ” & lngTableCount
Debug.Print “最適化されたフィールド数: ” & lngModifiedCount
Debug.Print “所要時間: ” & Format$(Timer – dblStartTime, “0.00秒”)

MsgBox “Unicode圧縮の最適化が完了しました。” & vbCrLf & _
“最適化されたフィールド数: ” & lngModifiedCount, vbInformation, “アーキテクチャ基盤”

CleanUp:
‘ メモリの明示的解放(VBAエンジンによるガベージコレクションの遅延を防ぐ)
Set fld = Nothing
Set tdf = Nothing
Set dbs = Nothing
Exit Sub

ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “Critical Error”
Resume CleanUp
End Sub

‘ ————————————————————————-
‘ 補助関数: プロパティの有無を安全に判定しつつ値を設定する
‘ ————————————————————————-
Private Function SetUnicodeCompressionSafe(ByRef fld As DAO.Field, ByVal val As Boolean) As Boolean
Const PROP_NAME As String = “UnicodeCompression”
Dim prp As DAO.Property
Dim isChanged As Boolean

isChanged = False
On Error GoTo ProcError

‘ 既存のプロパティ値と比較し、異なる場合のみ書き換える
If fld.Properties(PROP_NAME).Value <> val Then
fld.Properties(PROP_NAME).Value = val
isChanged = True
End If

SetUnicodeCompressionSafe = isChanged
Exit Function

ProcError:
‘ プロパティ自体が存在しない場合(通常dbTextには存在するが念のため)
If Err.Number = 3270 Then
Set prp = fld.CreateProperty(PROP_NAME, dbBoolean, val)
fld.Properties.Append prp
Set prp = Nothing
SetUnicodeCompressionSafe = True
Else
‘ その他の予期せぬエラー
SetUnicodeCompressionSafe = False
End If
End Function

—

3. チーフアーキテククトが解説するコードの急所

このコードが単なる「動くだけのスクリプト」ではなく、プロフェッショナルな基盤コードたる所以を解説する。

① DAOとADOの使い分けの哲学

テーブルの構造(スキーマ)を定義・変更する領域においては、ADO(ActiveX Data Objects)ではなく、DAOをファーストチョイスしなければならない。ADOの `ADOX` を使ったスキーマ操作は、Accessのローカルエンジン(ACE/Jet)において冗長であり、一部のプロパティ(Access固有の細かい定義)へのアクセスで制限を受ける。DAOこそがAccessのネイティブ言語であることを忘れてはならない。

② プロパティの動的生成(Error 3270の捕捉)

AccessのDAOプロパティは、明示的に参照するまでインスタンス化されていない場合がある(遅延バインディング的な挙動)。存在しないプロパティにアクセスすると `Error 3270: Property not found` が発生する。
上記の `SetUnicodeCompressionSafe` 関数では、このエラーを逆手に取り、プロパティが存在しない場合は `CreateProperty` で動的に生成・追加する堅牢な構造(Defensive Programming)を採用している。

③ オブジェクトのライフサイクル管理

`For Each` ループ内で取得した `TableDef` や `Field` オブジェクト、そして変数 `dbs` は、処理の終了時に必ず `Set xxx = Nothing` で明示的に解放している。
これを怠ると、VBAの内部参照カウンターが残り続け、MDB/ACCDBファイルがロックされたり、メモリリークを引き起こしてAccess全体の動作が不安定になる。シニアエンジニアにとって「使い終わった器を綺麗に洗う」のは鉄則である。

—

4. 運用上の注意点とさらなる高みへ

このスクリプトを本番環境に適用する際、以下の実務的知見を心に刻んでおいてほしい。

1. 実行前のバックアップは絶対
テーブル定義をプログラムから一括変更するため、実行前には必ず実ファイル(.accdb)の物理バックアップを取ること。
2. インデックスへの影響
文字列の格納サイズが変わるだけであり、インデックス自体の構造やB-Treeのロジックには影響を与えない。したがって、インデックスの再構築(`CompactDatabase`)を別途行う必要はないが、本スクリプト実行後に「データベースの最適化(Compact & Repair)」を合わせて実施することで、ファイルサイズそのものを劇的に縮小させることが可能だ。

データベースの肥大化に悩むフェーズは、これで終わりにする。
コードをデプロイし、スリム化されたACCDBが軽快に疾走する音を聞け。それが、アーキテククトの仕事だ。

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