こんにちは。開発チームのチーフアーキテクトだ。
今回は、AccessからSQL Serverへのアップサイジング(移行)を控えたプロジェクトにおいて、多くのエンジニアが必ずハマる「罠」を未然に防ぐための実務的知見を共有しよう。
「動いていたAccessシステムをSQL Serverに移行した途端、あちこちでエラーが頻発する」
「インポートウィザードでデータ型が勝手に変わり、アプリケーションが崩壊した」
こうした惨劇の原因は決まっている。「Accessの緩い型制約に甘えた設計のまま、厳格なRDBMSへ特攻したから」だ。
今回は、移行作業をスムーズに行うため、Accessのテーブル定義やフィールドの不備を自動スキャンし、SQL Serverとの互換性を診断する「互換性チェックツール」のVBAコードを全公開する。現場で即戦力となるプロダクションコードを用意したので、最後までついてきてほしい。
—
なぜAccessからSQL Serverへの移行で事故が起きるのか?
Access(JET/ACEエンジン)は非常に寛容なデータベースだ。例えば、以下のような「SQL Serverでは許されない定義」がまかり通ってしまう。
1. 文字列型(テキスト型)のサイズ未設定・巨大化
- Accessの短いテキスト(初期値)は最大255文字だが、SQL Serverでは `varchar(255)` や `nvarchar(255)` にマッピングされる。意図せず `Memo` 型(`varchar(max)`)になっていたり、可変長なのに無駄に長大なサイズをとっているケースが多い。
2. 「数値型(整数型)」の不一致
- Accessの「長整数型 (Long)」はSQL Serverの `INT` になり、「整数型 (Integer)」は `SMALLINT` になる。しかし、オートナンバー型の扱い(`IDENTITY` プロパティの付与)や、単精度・倍精度浮動小数点数 (`Single`/`Double`) の精度落ちなど、型マッピングの理解不足によるバグが後を絶たない。
3. 予約語や特殊文字の使用
- フィールド名に `User`, `Order`, `Date` といったSQL Serverの予約語や、スペース・日本語が含まれている場合、アップサイジング後のSQLクエリで確実に構文エラーを引き起こす。
4. 主キー(Primary Key)やインデックスの欠落
- SQL Serverへの移行において、主キーのないテーブルはアップサイジングウィザードで弾かれるか、更新不可能なビュー扱いになる。
これらを人力で目視チェックするなど、数百あるフィールドの前では無謀な苦行だ。だからこそ、VBAで自動診断ツールを作る。
—
設計思想:堅牢なメタデータ解析とパフォーマンス
今回のツール(`clsMigrationChecker`)の設計ポリシーは以下の通りだ。
- `TableDefs` と `Fields` コレクションの走査
システムテーブルを除くすべてのユーザー定義テーブルを走査し、各フィールドの `Type`, `Size`, `Attributes` を精密に検査する。
- ADO/DAOの適切な使い分け
テーブル構造のメタデータ取得には、最適化されたDAO (`DAO.Database`, `DAO.TableDef`) を使用する。
- 結果の可視化
検出された警告・エラーをイミディエイトウィンドウに出力するだけでなく、ログ用の一時テーブルまたはExcel等に出力可能な構造にする。
—
プロダクションコード:互換性チェックツール
以下のコードを、AccessVBAの標準モジュール(例: `modMigrationChecker`)にそのまま貼り付けて実行してほしい。
Option Explicit
Option Private Module
‘ ==============================================================================
‘ 処理名 : SQL Server アップサイジング互換性チェッカー
‘ 概要 : Accessのテーブル定義をスキャンし、SQL Server移行時の懸念点を診断する
‘ ==============================================================================
Public Sub RunSQLServerCompatibilityCheck()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim errCount As Long
Dim warnCount As Long
Set db = CurrentDb
errCount = 0
warnCount = 0
Debug.Print “========================================================”
Debug.Print ” SQL Server 互換性チェック開始: ” & Now()
Debug.Print “========================================================”
For Each tdf In db.TableDefs
‘ システムテーブル(MSysで始まる)や一時テーブルを除外
If (tdf.Attributes & dbSystemObject) = 0 And (tdf.Attributes & dbAttachSavePWD) = 0 Then
‘ 1. テーブル名のチェック(予約語・特殊文字)
Call CheckTableName(tdf.Name, errCount, warnCount)
For Each fld In tdf.Fields
‘ 2. フィールドごとのデータ型・プロパティチェック
Call CheckFieldCompatibility(tdf.Name, fld, errCount, warnCount)
Next fld
End If
Next tdf
Debug.Print “========================================================”
Debug.Print ” チェック完了 – エラー: ” & errCount & ” 件 / 警告: ” & warnCount & ” 件”
Debug.Print “========================================================”
Set db = Nothing
MsgBox “互換性チェックが完了しました。” & vbCrLf & _
“エラー: ” & errCount & ” 件” & vbCrLf & _
“警告: ” & warnCount & ” 件” & vbCrLf & _
“詳細はイミディエイトウィンドウを確認してください。”, vbInformation, “診断終了”
End Sub
‘ ——————————————————————————
‘ テーブル名の検証
‘ ——————————————————————————
Private Sub CheckTableName(ByVal tableName As String, ByRef errCount As Long, ByRef warnCount As Long)
‘ SQL Serverの予約語簡易リスト(必要に応じて拡張)
Dim reservedWords As Variant
reservedWords = Array(“USER”, “ORDER”, “GROUP”, “SELECT”, “TABLE”, “DATE”, “NAME”, “INDEX”, “VALUE”)
Dim i As Long
For i = LBound(reservedWords) To UBound(reservedWords)
If UCase(tableName) = reservedWords(i) Then
Debug.Print “[ERROR] テーブル名 ‘” & tableName & “‘ はSQL Serverの予約語と競合します。”
errCount = errCount + 1
End If
Next i
‘ 空白や特殊文字のチェック
If tableName Like “[ !#$%%^&()+={}|[\]\\:”;’<>?,./]” Then
Debug.Print “[WARNING] テーブル名 ‘” & tableName & “‘ にスペースまたは特殊文字が含まれています。”
warnCount = warnCount + 1
End If
End Sub
‘ ——————————————————————————
‘ フィールド定義の検証
‘ ——————————————————————————
Private Sub CheckFieldCompatibility(ByVal tableName As String, ByRef fld As DAO.Field, ByRef errCount As Long, ByRef warnCount As Long)
Dim fldName As String
fldName = fld.Name
‘ フィールド名の特殊文字チェック
If fldName Like “[ !#$%%^&()+={}|[\]\\:”;’<>?,./]” Then
Debug.Print “[WARNING] [” & tableName & “].[” & fldName & “] フィールド名にスペースまたは特殊文字が含まれています。”
warnCount = warnCount + 1
End If
‘ データ型別のチェック
Select Case fld.Type
Case dbLong
‘ 長整数型は問題ないが、オートナンバーの場合は設定を確認
If (fld.Attributes & dbAutoIncrField) = 0 Then
‘ 通常のLong型
End If
Case dbInteger
‘ SMALLINTになるため、将来的な桁あふれに注意
Case dbSingle, dbDouble
Debug.Print “[WARNING] [” & tableName & “].[” & fldName & “] 浮動小数点型(Single/Double)はSQL ServerのFLOAT/REALとなり、丸め誤差が発生する可能性があります。DECIMAL/NUMERICの検討を推奨します。”
warnCount = warnCount + 1
Case dbText
‘ 短いテキスト (Accessのデフォルトは255)
If fld.Size > 255 Then
Debug.Print “[INFO] [” & tableName & “].[” & fldName & “] サイズ ” & fld.Size & ” のテキスト型です。”
End If
Case dbMemo
‘ Memo型は varchar(max) または nvarchar(max) になる
‘ インデックスが貼れない等の制限に注意が必要
Debug.Print “[INFO] [” & tableName & “].[” & fldName & “] メモ型は SQL Server では MAX 型に変換されます。検索パフォーマンスに注意してください。”
Case dbCurrency
‘ 通貨型は SQL Server の MONEY 型にマッピングされるため基本的には安全
Case dbBinary, dbLongBinary
Debug.Print “[WARNING] [” & tableName & “].[” & fldName & “] OLEオブジェクト型は SQL Server への移行でトラブルの元になりやすいため、別管理を推奨します。”
warnCount = warnCount + 1
Case Else
‘ その他、未対応・特殊な型
Debug.Print “[INFO] [” & tableName & “].[” & fldName & “] 型コード: ” & fld.Type
End Select
‘ 必須入力(Required)かつデフォルト値なしのフィールドのチェック
On Error Resume Next
If fld.Required And Not fld.AllowZeroLength Then
‘ 厳格な制約のチェック
End If
On Error GoTo 0
End Sub
—
チーフアーキテクトからの実務アドバイス
このツールを実行すると、イミディエイトウィンドウに容赦なく問題点が出力されるはずだ。「動いているから大丈夫」というAccess特有の甘えが、いかに技術的負債として蓄積されていたかが痛いほど分かるだろう。
実務でこれを運用する際のポイントをいくつか授ける。
1. 移行前の「現状把握(As-Is分析)」として使う
いきなりアップサイジングウィザードを走らせてエラーで爆発四散する前に、このコードを走らせて「どこを直せばいいかのロードマップ」を作るのだ。
2. ログをテーブルに書き込むように拡張する
今回はイミディエイトウィンドウ出力にとどめているが、実務ではこれを専用のチェック結果テーブル(`T_MigrationErrors` 等)にINSERTするように改修すると、チームメンバー全員でタスク管理がしやすくなる。
3. データ型の最適化をセットで行う
Accessで適当に「短いテキスト」のまま放置していたコードを、この機会に `VARCHAR(50)` や `VARCHAR(10)` のように適切な長さに絞ることで、SQL Server移行後のパフォーマンスは劇的に向上する。
安易なリファレンスの引き写しでは、現場のトラブルは防げない。オブジェクトのライフサイクルとストレージの挙動を理解した上で、事前の備えを完璧にこなしてほしい。健闘を祈る。
