AccessのTransferDatabaseでバックアップDBへテーブルを上書きコピーする方法――エラー3024/重複テーブル対策

Accessアプリの終了時に、ローカルのメインDBからネットワーク上のバックアップDBへテーブルを確実にコピー(上書き)したい――その要件はシンプルに見えて、DoCmd.TransferDatabase の引数の取り違えやネットワーク制約、ロックの影響でつまずきがちです。本記事では、実際のパスとテーブル名を使った再現性の高いコード、増殖テーブル対策、エラー3024の切り分け、そして安全な二段階入れ替え方式まで、現場で実戦投入できる手順をまとめて解説します。

目次

前提とゴール

  • 元DB(メイン):C:\Users\Mikemoo\Desktop\PGA Matrix.accdb
  • バックアップ先DB:Z:\CSC\MMOORE\ACCESS\Backup\PGA MATRIX 6‑2022 Rep Version.accdb
  • テーブル名:Main Data
  • アプリ終了時(フォームの Unload / Close、もしくは AutoExec マクロ終了時など)にコピーを実行

本記事の最終目標は、「エラー3024を出さず」「MainData1, MainData2… のような重複テーブルを作らず」「既存テーブルを確実に置き換える」ことです。

症状と原因の整理

症状主な原因ポイント
実行時エラー 3024(ファイルが見つかりません)TransferDatabase の第3引数にコピー先ではなく誤って元DBを渡している/パスの誤り/ドライブ割り当てがない第3引数 DatabaseName はコピー先(エクスポート先)の ファイル を示す
MainData1, MainData2… と重複テーブルが増殖先に同名テーブルが存在し、Access が競合回避のため自動リネームコピー前に該当テーブルを削除する「事前削除」または「二段階入れ替え」を採用
「上書きしたはずなのに内容が変わらない」バックアップ先DBが開きっぱなしでロック/別ユーザーが使用中/書き込み権限なし排他ファイル(.laccdb)や共有状況、権限を確認
たまにだけ失敗するネットワーク断・スリープ復帰直後・ローミング環境でZ:が未マウントUNC パスも選択肢。事前にフォルダ書き込みテストを行う

TransferDatabase の正しい呼び出し(最小コード)

元DB(現在開いているDB)から、バックアップDBへテーブルをエクスポートする最小コードです。第3引数がコピー先ファイルであることに注意してください。

Option Compare Database
Option Explicit

Public Sub Export_MainData_To_Backup_Min()
Dim strDest As String
strDest = "Z:\CSC\MMOORE\ACCESS\Backup\PGA MATRIX 6-2022 Rep Version.accdb"


' Main Data → 同名 Main Data としてエクスポート
DoCmd.TransferDatabase _
    acExport, _               ' エクスポート
    "Microsoft Access", _     ' 種別
    strDest, _                ' コピー先 .accdb
    acTable, _                ' オブジェクト種別
    "Main Data", _            ' 元テーブル名
    "Main Data"               ' 先テーブル名


End Sub 

引数の対応関係(早見表)

引数指定例意味注意点
TransferTypeacExport「現在のDB」→「外部DBへ」コピーインポートの場合は acImport
DatabaseType"Microsoft Access"コピー先/元のDB種別Access 間コピーならこれでOK
DatabaseNameバックアップ先の .accdb パスエクスポート時はコピー先ファイルここを取り違えると 3024 の温床
ObjectTypeacTableテーブルを対象クエリやフォームも可
Source"Main Data"現在DBの元テーブル名スペースはそのままOK
Destination"Main Data"先DBに作成(上書き)する名称既存有りだと自動リネームされる場合あり
StructureOnlyFalse(既定)データ含む/含まないTrue だと「定義のみ」

「テーブルが増える」問題を止める確実策:事前削除→コピー

既存の Main Data がバックアップDBに残っていると、Access が衝突回避のために Main Data1 などを作成してしまうことがあります。コピー前に対象テーブルを明示的に削除(存在しなければ無視)してからエクスポートすれば、増殖は止まります。

Public Sub Backup_MainData_Overwrite()
    Dim dbs As DAO.Database
    Dim strDest As String
    strDest = "Z:\CSC\MMOORE\ACCESS\Backup\PGA MATRIX 6-2022 Rep Version.accdb"


' 先DBを開いて事前削除
Set dbs = OpenDatabase(strDest)
On Error Resume Next
dbs.Execute "DROP TABLE [Main Data]"    ' 無ければスルー
On Error GoTo 0
dbs.Close

' 改めて転送
DoCmd.TransferDatabase acExport, "Microsoft Access", strDest, acTable, "Main Data", "Main Data"


End Sub 

ポイント:

  • DROP TABLE は存在しない時にエラーが出るため、On Error Resume Next でいったん握りつぶしてOK。直後に On Error GoTo 0 で戻すのを忘れないこと。
  • リレーションや外部キーで依存している場合は削除に失敗します。バックアップ専用DBでは、バックアップ対象テーブルにリレーションを張らない設計が無難です。

さらに堅牢に:安全な「二段階入れ替え」パターン

ネットワーク断や予期せぬ中断でコピーが中途半端に終わると、先テーブルが消えたまま…という事態を避けたいときは、テンポラリ名で転送→検証→名前入れ替えの順で行うと安全です。

Public Sub Backup_MainData_SafeSwap()
    Const SRC As String = "Main Data"
    Const DEST As String = "Main Data"


Dim destPath As String
destPath = "Z:\CSC\MMOORE\ACCESS\Backup\PGA MATRIX 6-2022 Rep Version.accdb"

Dim tmpName As String
tmpName = DEST & "_tmp_" & Format(Now, "yyyymmdd_hhnnss")

' 1) 一時名で転送
DoCmd.TransferDatabase acExport, "Microsoft Access", destPath, acTable, SRC, tmpName

' 2) レコード件数チェック(任意)
Dim cntSrc As Long, cntTmp As Long
cntSrc = DCount("*", "[" & SRC & "]")
cntTmp = CountTableRecordsInExternal(destPath, tmpName)
If cntSrc <> cntTmp Then
    Err.Raise vbObjectError + 5000, , "件数不一致: src=" & cntSrc & ", tmp=" & cntTmp
End If

' 3) 先DBで入れ替え(旧を削除→tmp を正式名にリネーム)
Dim dbs As DAO.Database
Set dbs = OpenDatabase(destPath)
On Error Resume Next
dbs.TableDefs.Delete DEST
On Error GoTo 0
dbs.TableDefs(tmpName).Name = DEST
dbs.Close


End Sub

Private Function CountTableRecordsInExternal(ByVal dbPath As String, ByVal tbl As String) As Long
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = OpenDatabase(dbPath)
Set rs = db.OpenRecordset("SELECT COUNT(*) AS Cnt FROM [" & tbl & "]")
CountTableRecordsInExternal = rs!Cnt
rs.Close: db.Close
End Function 

この方式なら、コピー途中で失敗しても既存の正式テーブルは残るため、復旧が容易です。

パス/権限/ロックの切り分け手順(エラー3024対策)

動作確認は「ローカル→ローカル」→「ローカル→ネットワーク」→「ネットワーク→ネットワーク」の順で段階的に進めます。以下のチェック関数を先に走らせ、エラーを潰すと調査が早く終わります。

Public Function CanWriteFolder(ByVal folderPath As String) As Boolean
    On Error GoTo EH
    Dim f As Integer, testFile As String
    If Right$(folderPath, 1) = "\" Then
        testFile = folderPath &amp; "__writetest.tmp"
    Else
        testFile = folderPath &amp; "\__writetest.tmp"
    End If
    f = FreeFile
    Open testFile For Output As #f
    Print #f, "test"
    Close #f
    Kill testFile
    CanWriteFolder = True
    Exit Function
EH:
    CanWriteFolder = False
End Function
  • ドライブ割当(Z:)が無い:サービスやタスクスケジューラ経由だと、ユーザーセッションと異なるためZ:が未割り当てのケースがあります。可能なら UNC(例:\\Server\Share\CSC\MMOORE\ACCESS\Backup\...)を使用。
  • フォルダ権限:読み取りだけでなく書き込み/削除権限が必要。上記の CanWriteFolder で判定。
  • ロック:バックアップ先DBが開きっぱなしだと .laccdb が生成され、書き込みに失敗することがあります。コピー前に閉じる運用に。

スペース・全角半角・大文字小文字の差異に注意

テーブル名やファイル名にスペースが含まれていても TransferDatabase は問題なく扱えますが、SQL の DROP TABLE、DCount などで参照するときは必ず角括弧 [ ] で囲みます。また、見た目が同じでも全角空白や末尾空白が混ざると別名とみなされます。以下のユーティリティで目視しづらい空白を検出できます。

Public Function ShowCodePoints(ByVal s As String) As String
    Dim i As Long, ch As String, out As String
    For i = 1 To Len(s)
        ch = Mid$(s, i, 1)
        out = out &amp; Right$("0000" &amp; Hex(AscW(ch)), 4) &amp; " "
    Next
    ShowCodePoints = Trim$(out)
End Function

終了時に自動でバックアップする実装例(イベント連動)

メインフォームの Form_Unload などにフックして、アプリ終了時にバックアップを走らせます。失敗時のログ蓄積も併せて仕込んでおくと保守が楽です。

Private Sub Form_Unload(Cancel As Integer)
    On Error GoTo EH
    Backup_MainData_SafeSwap
    AppendLog "Backup OK"
    Exit Sub
EH:
    AppendLog "Backup NG: " & Err.Number & " - " & Err.Description
End Sub

Private Sub AppendLog(ByVal msg As String)
Dim f As Integer
f = FreeFile
Open Environ$("TEMP") & "\PGA_Matrix_Backup.log" For Append As #f
Print #f, Format$(Now, "yyyy-mm-dd hh:nn:ss") & " " & msg
Close #f
End Sub 

ファイル全体のバックアップも選択肢

「テーブル単体の同期」ではなく「DBファイル全体を退避」したいだけなら、FileCopy が最もシンプルです。アプリ終了直前に下記を呼ぶだけで済みます。

Public Sub Backup_FileCopy_All()
    Dim src As String, dest As String
    src = "C:\Users\Mikemoo\Desktop\PGA Matrix.accdb"
    dest = "Z:\CSC\MMOORE\ACCESS\Backup\PGA MATRIX 6-2022 Rep Version.accdb"


' 既存バックアップを時刻付きで退避してから上書きしたい場合
Dim bak As String
bak = dest & "." & Format$(Now, "yyyymmdd_hhnnss") & ".bak"
On Error Resume Next
Name dest As bak              ' 失敗しても続行
On Error GoTo 0

FileCopy src, dest


End Sub 

利点:シンプル・高速・テーブル依存なし。欠点:サイズが大きい場合の転送コスト、他のユーザーが先DBを開いていると失敗しやすい。

よくあるエラーコードと対処

エラー主な原因対処
3024: ファイルが見つかりません第3引数取り違え/パス誤記/Z:未マウント第3引数は先DBのフルパス。UNCで再試行
3044: パスが無効です存在しないフォルダ/権限不足事前にフォルダを作成/CanWriteFolder で検証
3045: ファイルは別のユーザーが使用中先DBが開いている/.laccdb 残留先DBを閉じる/残留ロックを削除(全員退出後)
3211: テーブルをロックできません他プロセスが該当テーブルを使用中時間をずらす/依存オブジェクトを解除
3033: 権限が足りません共有フォルダのACL設定書き込み・削除・リネーム権限を付与

実用ユーティリティ:共通化して使い回す

プロジェクトで再利用できるよう、パス検証、事前削除、転送、入れ替え、ログをひとまとめにしたラッパーを用意しておくと便利です。

Public Sub ExportTableWithOverwrite( _
    ByVal destAccdb As String, _
    ByVal srcTable As String, _
    Optional ByVal destTable As String = vbNullString, _
    Optional ByVal safeSwap As Boolean = True)


If destTable = vbNullString Then destTable = srcTable

' 先DBの存在チェック
If Dir$(destAccdb, vbNormal) = vbNullString Then
    Err.Raise 3024, , "先DBが見つかりません: " & destAccdb
End If

If safeSwap Then
    Dim tmpName As String
    tmpName = destTable & "_tmp_" & Format$(Now, "yyyymmdd_hhnnss")

    DoCmd.TransferDatabase acExport, "Microsoft Access", destAccdb, acTable, srcTable, tmpName

    ' 簡易検証(件数一致)
    Dim cntSrc As Long, cntTmp As Long
    cntSrc = DCount("*", "[" & srcTable & "]")
    cntTmp = CountTableRecordsInExternal(destAccdb, tmpName)
    If cntSrc <> cntTmp Then
        ' 失敗時はテンポラリを後始末
        Dim dbs As DAO.Database
        Set dbs = OpenDatabase(destAccdb)
        On Error Resume Next
        dbs.TableDefs.Delete tmpName
        On Error GoTo 0
        dbs.Close
        Err.Raise vbObjectError + 5001, , "検証失敗(件数不一致)"
    End If

    ' 入れ替え
    Dim dbs2 As DAO.Database
    Set dbs2 = OpenDatabase(destAccdb)
    On Error Resume Next
    dbs2.TableDefs.Delete destTable
    On Error GoTo 0
    dbs2.TableDefs(tmpName).Name = destTable
    dbs2.Close
Else
    ' 事前削除→一発転送
    Dim dbs3 As DAO.Database
    Set dbs3 = OpenDatabase(destAccdb)
    On Error Resume Next
    dbs3.TableDefs.Delete destTable
    On Error GoTo 0
    dbs3.Close

    DoCmd.TransferDatabase acExport, "Microsoft Access", destAccdb, acTable, srcTable, destTable
End If


End Sub 

運用のヒント(落とし穴を避けるコツ)

  • 文字列は変数に入れてから渡す:長いフルパスに空白が混じっても、変数経由ならタイプミスが減ります。
  • バックアップDBを固定:世代管理が必要なら「テーブル名に日付列を持つ」方針も検討。毎回別DBを作らないとロックや肥大化が減ります。
  • コンパクト最適化:世代管理しないなら、バックアップDB側に定期的な Compact を実施。肥大化抑制に有効。
  • 参照設定:Microsoft Office XX.0 Access database engine Object Library にチェック(DAO早期バインディングを使う場合)。
  • タスクスケジューラ実行:ユーザーコンテキストが変わるため Z: が無いことが多い。UNC に変更するか、起動時にマップドライブを作成する。

検証チェックリスト

  • ローカル→ローカル(例:C:\Temp\dest.accdb)で成功するか
  • フォルダ書き込みテスト(CanWriteFolder)が通るか
  • バックアップ先DBが開いていないか(.laccdb の有無)
  • 事前削除または二段階入れ替えで重複テーブルが消えるか
  • 件数検証で元と同数か(差分があれば制約・既定値・拒否された行を調査)
  • エラー時にログが残るか(復旧時の手がかり)

フルサンプル:終了時バックアップを丸ごと組み込む

以下を標準モジュールに置き、終了時イベントから RunBackup を呼び出します。実運用レベルの詳細ログ、二段階入れ替え、エラー捕捉を一式含みます。

Option Compare Database
Option Explicit

Public Sub RunBackup()
Dim dest As String
dest = "Z:\CSC\MMOORE\ACCESS\Backup\PGA MATRIX 6-2022 Rep Version.accdb"


On Error GoTo EH
LogMsg "Backup start"
If Dir$(dest, vbNormal) = vbNullString Then
    Err.Raise 3024, , "先DBが見つかりません: " & dest
End If

If Not CanWriteFolder(GetFolderOfPath(dest)) Then
    Err.Raise vbObjectError + 5100, , "書き込み不可: " & GetFolderOfPath(dest)
End If

ExportTableWithOverwrite dest, "Main Data", "Main Data", True
LogMsg "Backup done"
Exit Sub


EH:
LogMsg "Backup failed: " & Err.Number & " - " & Err.Description
End Sub

Private Function GetFolderOfPath(ByVal p As String) As String
Dim i As Long: i = InStrRev(p, "")
If i > 0 Then GetFolderOfPath = Left$(p, i - 1)
End Function

Private Sub LogMsg(ByVal msg As String)
Dim f As Integer
f = FreeFile
Open Environ$("TEMP") & "\PGA_Matrix_Backup.log" For Append As #f
Print #f, Format$(Now, "yyyy-mm-dd hh:nn:ss"), msg
Close #f
End Sub 

まとめ:これで「確実に」「静かに」バックアップできる

  • 第3引数はコピー先ファイル:エクスポート時はここを間違えない。
  • 重複テーブル対策:事前削除、または二段階入れ替え(テンポラリ→検証→リネーム)。
  • ネットワークは切り分け優先:ローカルで成功→UNC で再検証。Z: の存在と書き込み権限をチェック。
  • ログ常設:成功/失敗を記録し、あとから追跡できるように。

上記の手順とサンプルをそのまま組み込めば、アプリ終了時に Main Data を確実にバックアップDBへコピーし、エラー3024や重複テーブルの悩みから解放されます。まずはローカルで動作を固め、ネットワークに段階展開するのが成功の近道です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次