- ねらい:Excel の一覧を「DB の正本」と自動で同期させる“型”を作る
- 設計:Excel 側で「同期対象」と「操作種別」をはっきり持つ
- 共通基盤:ADO接続・SQL実行・ログのユーティリティ
- 行ごとの INSERT/UPDATE/DELETE を切り替えるロジック
- Excel → DB 同期本体:Customers シートを見て順番に反映する
- シートとテーブルのマッピングを柔軟にする(列名が変わっても壊れにくく)
- 例題:Excel側で顧客マスタを編集 → DB と同期する一連の流れ
- 落とし穴と対策(重要ポイントの深掘り)
- まとめ:Excel は“編集画面”、DB は“正本”として、同期ロジックをテンプレ化する
ねらい:Excel の一覧を「DB の正本」と自動で同期させる“型”を作る
Excel でマスタや明細を管理しつつ、最終的には SQL Server/Access/MySQL などの DB を正本にしたい場面は多いです。
手作業でインポートするのではなく、
- Excel で編集
- ボタン1つで「追加/更新/削除」を DB に反映
という“Excel → DB 同期”の型を作っておくと、運用が一気に楽になります。
ここでは初心者向けに、
- 基本の考え方(Excel 側の「同期フラグ」+ DB 側のキー)
- ADO で DB に接続するテンプレ
- 行ごとの「INSERT/UPDATE/DELETE」を切り替えるロジック
- シートとテーブルの項目マッピング
- 具体例(顧客マスタの同期)
を、コード付きでかみ砕いて説明します。
設計:Excel 側で「同期対象」と「操作種別」をはっきり持つ
シート構成(CustomerSheet のイメージ)
例えば顧客マスタを同期したいとします。
Excel シート「Customers」を以下のような構成にします。
A列: SyncFlag(同期対象か。Y/N)
B列: Action(操作種別。INS/UPD/DEL)
C列: CustomerID(主キー。DB 側の PK と一致)
D列: CustomerName
E列: Email
F列: Phone
ポイントは 2 つです。
1つ目:全行を自動で判定しない。SyncFlag=Y の行だけ DB に反映するようにし、「送る/送らない」を人間が制御できるようにする。
2つ目:Action に INS/UPD/DEL を明示しておく。これで VBA 側は「何をするか」を迷わずに済みます。
共通基盤:ADO接続・SQL実行・ログのユーティリティ
ADO 接続テンプレ(SQL Server の例)
環境に合わせて ConnectionString を書き換えてください(Access や SQLite でも同じ型でいけます)。
' ModDb_Base.bas
Option Explicit
Public Function GetDbConnection() As Object
' ここでは SQL Server を例とします
' 実際の接続文字列は環境に合わせて変更してください
Dim conn As Object
Set conn = CreateObject("ADODB.Connection")
Dim connStr As String
connStr = ""
connStr = connStr & "Provider=SQLOLEDB;"
connStr = connStr & "Data Source=SERVER_NAME;"
connStr = connStr & "Initial Catalog=DB_NAME;"
connStr = connStr & "Integrated Security=SSPI;" ' または User ID=...;Password=...;
conn.Open connStr
Set GetDbConnection = conn
End Function
Public Sub ExecNonQuery(ByVal conn As Object, ByVal sql As String)
' INSERT/UPDATE/DELETE など、結果セットを返さない SQL 用
Dim cmd As Object
Set cmd = CreateObject("ADODB.Command")
With cmd
.ActiveConnection = conn
.CommandType = 1 ' adCmdText
.CommandText = sql
.Execute
End With
End Sub
VBここではシンプルさを優先して「直接 SQL を組み立てて流す」形にしています。
本番ではパラメータを使うとさらに安全ですが、「まず動く形」を優先します。
ログ出力(シート右側に結果を残す)
Public Sub WriteSyncLog(ByVal ws As Worksheet, ByVal rowIdx As Long, ByVal status As String, ByVal message As String)
' ここでは G列=Status, H列=Message と仮定
ws.Cells(rowIdx, 7).Value = status
ws.Cells(rowIdx, 8).Value = message
End Sub
VBこのように、「どの行で何をしたか/成功か失敗か」をログに残すことで、後から必ず確認できるようにします。
行ごとの INSERT/UPDATE/DELETE を切り替えるロジック
SQL を組み立てる小さな関数(危険ポイントを理解する)
' ModDb_SqlBuilder.bas
Option Explicit
Private Function SqlEscape(ByVal v As String) As String
' シングルクォートをエスケープ
SqlEscape = Replace(v, "'", "''")
End Function
Public Function BuildInsertCustomerSql(ByVal customerId As String, _
ByVal name As String, _
ByVal email As String, _
ByVal phone As String) As String
Dim sql As String
sql = "INSERT INTO Customers (CustomerID, CustomerName, Email, Phone) VALUES ("
sql = sql & "'" & SqlEscape(customerId) & "',"
sql = sql & "'" & SqlEscape(name) & "',"
sql = sql & "'" & SqlEscape(email) & "',"
sql = sql & "'" & SqlEscape(phone) & "')"
BuildInsertCustomerSql = sql
End Function
Public Function BuildUpdateCustomerSql(ByVal customerId As String, _
ByVal name As String, _
ByVal email As String, _
ByVal phone As String) As String
Dim sql As String
sql = "UPDATE Customers SET "
sql = sql & "CustomerName='" & SqlEscape(name) & "',"
sql = sql & "Email='" & SqlEscape(email) & "',"
sql = sql & "Phone='" & SqlEscape(phone) & "' "
sql = sql & "WHERE CustomerID='" & SqlEscape(customerId) & "'"
BuildUpdateCustomerSql = sql
End Function
Public Function BuildDeleteCustomerSql(ByVal customerId As String) As String
Dim sql As String
sql = "DELETE FROM Customers WHERE CustomerID='" & SqlEscape(customerId) & "'"
BuildDeleteCustomerSql = sql
End Function
VB重要なのは SqlEscape です。
文字列中の '(シングルクォート)を '' に置き換えないと、SQLが壊れます(最悪、SQLインジェクションの入り口にもなります)。
本格運用ではパラメータ化必須ですが、まずはシングルクォートのエスケープだけは“絶対に入れる”癖をつけてください。
Excel → DB 同期本体:Customers シートを見て順番に反映する
一括同期マクロ(Insert/Update/Delete 切り替え)
' ModDb_SyncCustomers.bas
Option Explicit
Public Sub Sync_Customers()
Dim ws As Worksheet
Set ws = Worksheets("Customers")
Dim a As Variant
a = ws.Range("A1").CurrentRegion.Value
Dim conn As Object
Set conn = GetDbConnection()
Dim r As Long
For r = 2 To UBound(a, 1)
Dim flag As String
flag = UCase$(Trim$(CStr(a(r, 1)))) ' SyncFlag
If flag = "Y" Then
Dim act As String
act = UCase$(Trim$(CStr(a(r, 2)))) ' Action: INS/UPD/DEL
Dim customerId As String
Dim name As String
Dim email As String
Dim phone As String
customerId = CStr(a(r, 3))
name = CStr(a(r, 4))
email = CStr(a(r, 5))
phone = CStr(a(r, 6))
If Len(customerId) = 0 Then
Call WriteSyncLog(ws, r, "NG", "CustomerID が空です")
GoTo NextRow
End If
On Error GoTo ErrHandler
Dim sql As String
Select Case act
Case "INS"
sql = BuildInsertCustomerSql(customerId, name, email, phone)
ExecNonQuery conn, sql
Call WriteSyncLog(ws, r, "OK", "Insert 完了")
Case "UPD"
sql = BuildUpdateCustomerSql(customerId, name, email, phone)
ExecNonQuery conn, sql
Call WriteSyncLog(ws, r, "OK", "Update 完了")
Case "DEL"
sql = BuildDeleteCustomerSql(customerId)
ExecNonQuery conn, sql
Call WriteSyncLog(ws, r, "OK", "Delete 完了")
Case Else
Call WriteSyncLog(ws, r, "NG", "Action が不正: " & act)
End Select
On Error GoTo 0
End If
NextRow:
Next r
conn.Close
Set conn = Nothing
MsgBox "Customers の DB 同期が完了しました。", vbInformation
Exit Sub
ErrHandler:
Call WriteSyncLog(ws, r, "NG", "DBエラー: " & Err.Number & " - " & Err.Description)
Err.Clear
Resume NextRow
End Sub
VBここでの重要ポイントを深掘りします。
SyncFlag が Y の行だけを対象にし、「今はこの行は流さない」といった制御を簡単にします。
Action によって、INS/UPD/DEL を切り替えています。これを Excel 側で明示しておくと、VBA 側のロジックが非常に単純になります。
エラーが出ても全体が止まらないように、行単位でエラーを Catch→ログ→次へ という流れにしています。
Status/Message を書き込むことで、「どの行が DB へ成功/失敗したか」「失敗の理由は何か」がすぐ分かります。
シートとテーブルのマッピングを柔軟にする(列名が変わっても壊れにくく)
列番号を「列名から探す」ヘルパー
列が増えたり順番が変わったりすると、a(r, 3) などの「ベタ列番号」はすぐに壊れます。
少しレベルを上げて、“列名から列番号を探す”ヘルパーを入れておくと耐性が上がります。
' ModDb_ColumnHelper.bas
Option Explicit
Public Function ColIndex(ByVal headers As Variant, ByVal name As String) As Long
Dim c As Long
For c = 1 To UBound(headers, 2)
If LCase$(Trim$(CStr(headers(1, c)))) = LCase$(Trim$(name)) Then
ColIndex = c
Exit Function
End If
Next
ColIndex = 0
End Function
VBこれを使う形で、Sync_Customers を書き換えるとこうなります。
Public Sub Sync_Customers_ByHeader()
Dim ws As Worksheet
Set ws = Worksheets("Customers")
Dim a As Variant
a = ws.Range("A1").CurrentRegion.Value
Dim idxSync As Long, idxAction As Long, idxId As Long, idxName As Long, idxMail As Long, idxPhone As Long
idxSync = ColIndex(a, "SyncFlag")
idxAction = ColIndex(a, "Action")
idxId = ColIndex(a, "CustomerID")
idxName = ColIndex(a, "CustomerName")
idxMail = ColIndex(a, "Email")
idxPhone = ColIndex(a, "Phone")
Dim conn As Object
Set conn = GetDbConnection()
Dim r As Long
For r = 2 To UBound(a, 1)
Dim flag As String
flag = UCase$(Trim$(CStr(a(r, idxSync))))
If flag = "Y" Then
Dim act As String
act = UCase$(Trim$(CStr(a(r, idxAction))))
Dim customerId As String
customerId = CStr(a(r, idxId))
Dim name As String
name = CStr(a(r, idxName))
Dim email As String
email = CStr(a(r, idxMail))
Dim phone As String
phone = CStr(a(r, idxPhone))
' 以下は前と同じ(省略)
End If
Next
conn.Close
Set conn = Nothing
End Sub
VBこれなら、列の順番を変えてもヘッダ名さえ合っていれば動き続けます。
現場運用で列追加が頻繁に起こる場合は、こちらの書き方がおすすめです。
例題:Excel側で顧客マスタを編集 → DB と同期する一連の流れ
想定シナリオ
DB 側の Customers テーブルは既に存在し、
CustomerID を主キーとして持っているとします(Excel の C列と一致)。
- 初回だけ DB から Excel にダウンロード(別マクロで SELECT→シート貼り付け)
- Excel 側で顧客名・メール・電話を編集
- 追加したい顧客には SyncFlag=Y, Action=INS を設定
- 既存顧客の修正は SyncFlag=Y, Action=UPD
- 削除したい顧客は SyncFlag=Y, Action=DEL
- Sync_Customers を実行
これで、Excel 上の編集内容が DB に反映されます。
反映後は、SyncFlag を空に戻す/Action をクリアするなどの運用ルールを決めておくとよいです。
落とし穴と対策(重要ポイントの深掘り)
直接本番 DB に書き込みに行かない
最初のうちは、必ず「テスト用 DB」「テスト用テーブル」で動作確認してください。
接続先 DB を変えただけで本番と同じコードになるので、
実際に SQL がどう発行されているか、期待通り INSERT/UPDATE/DELETE されているかをテスト環境でチェックしてから本番に切り替えましょう。
トランザクション(まとまり)をどう扱うか
本格的にやるなら、
- 100 行まとめて BEGIN TRAN → コミット → ロールバック
といったトランザクション制御を入れるのが理想です。
初心者向けにはまず「1行ずつ確実に同期する」形で慣れ、
必要になったタイミングで conn.BeginTrans / conn.CommitTrans / conn.RollbackTrans を入れていくのが良いです。
SQL 組み立てミスでデータがおかしくなる
SqlEscape を必ず通すこと。
特に、名前やメールに ' が入るケースは意外と多いです(O’Neil など)。
本番運用では、できるだけパラメータ化(cmd.Parameters)に移行していくことを強くおすすめします。
日付・数値の型が DB と合わない
今回は文字列項目だけの例にしましたが、日付や数値を扱う場合、
Excel 上の見た目と DB の型(DATE, DECIMAL など)が合わずにエラーになることがあります。
実務では、
- Excel 側では「文字列」として DB の期待するフォーマットを作る
- もしくはパラメータで型を合わせる
方向で設計するのが安全です。
まとめ:Excel は“編集画面”、DB は“正本”として、同期ロジックをテンプレ化する
Excel → DB 同期の肝は、
Excel 側で「同期フラグ」「操作種別(INS/UPD/DEL)」「主キー」をはっきり持つ。
VBA 側で ADO 接続→行ごとに SQL を発行し、ログを残す。
列名とテーブル名のマッピングをテンプレート化し、プロジェクトごとに差し替えるだけにする。
という“型”を作ることです。
