AccessからPostgreSQLのテーブルを編集する
PostgreSQLの「売上伝票」テーブルおよび「売上明細」テーブルのデータをAccessで取得し、修正を加えたのち、PostgreSQLに保存します。

テーブルを準備する
PostgreSQLで「sample」という名前のデータベースの中に「T売上伝票」、「T売上明細」の2つのテーブルを用意しました。「T売上伝票」、「T売上明細」は生データを保存するテーブルです。Accessには「WT売上伝票」、「WT売上明細」の2つのテーブルを用意しました。「TEMP売上伝票」、「TEMP売上明細」は一時的にAccessから編集後のデータを受け取るテーブルです。この一時テーブルからストアドプロシージャを使って、生データのテーブルにデータを書き込みます。「TEMP売上伝票」、「TEMP売上明細」はAccessで編集したデータをPostgreSQLに保存するときに生成し、保存後は削除します。
さらに「T売上伝票」の主キー値発番用に「T発番」という名前のテーブルを用意しました。


「T売上伝票」、「T売上明細」の間に連鎖削除を設定しました。

PostgreSQLにストアドプロシージャを準備する
PostgreSQLに「import売上情報」という名前のストアドプロシージャを用意しました。
これにより一時テーブルのデータを生データのテーブルに書き込みます。
CREATE OR REPLACE PROCEDURE public."import売上情報"() LANGUAGE plpgsql AS $$ BEGIN BEGIN LOCK TABLE "T売上伝票" IN ACCESS EXCLUSIVE MODE; LOCK TABLE "T売上明細" IN ACCESS EXCLUSIVE MODE; --T売上伝票更新------------------------------------------------ MERGE INTO "T売上伝票" AS A USING "TEMP売上伝票" AS B ON A.伝票番号 = B.伝票番号 WHEN MATCHED THEN UPDATE SET 日付 = B.日付 WHEN NOT MATCHED THEN INSERT (伝票番号, 日付) VALUES (B.伝票番号, B.日付); -------------------------------------------------------------- --T売上明細更新------------------------------------------------ MERGE INTO "T売上明細" AS C USING "TEMP売上明細" AS D ON C."明細ID" = D."明細ID" WHEN MATCHED AND D.削除=FALSE THEN UPDATE SET 商品コード = D.商品コード,数量 = D.数量 WHEN MATCHED AND D.削除=TRUE THEN DELETE WHEN NOT MATCHED AND D.削除=FALSE THEN INSERT (伝票番号, 商品コード,数量) VALUES (D.伝票番号, D.商品コード,D.数量); --------------------------------------------------------------- EXCEPTION WHEN OTHERS THEN RAISE WARNING 'エラー'; ROLLBACK; RETURN; END; COMMIT; END; $$
PostgreSQLに「SetID」という名前のストアドファンクションを用意しました。これにより「売上伝票」の主キー値を発番します。以下に「SetID」のコードを記載します。
CREATE OR REPLACE FUNCTION public."SetID"() RETURNS integer LANGUAGE plpgsql AS $$ DECLARE id integer; BEGIN BEGIN LOCK TABLE "T発番" IN ACCESS EXCLUSIVE MODE; SELECT 連番 INTO id FROM "T発番"; UPDATE "T発番" SET 連番=id+1; EXCEPTION WHEN OTHERS THEN RETURN -1; END; RETURN id; END; $$
フォームの準備
下のような「Fサンプル」という名前のフォームを作成しました。「伝票一覧」と「売上明細」はサブフォームです。「売上伝票」の部分には非連結のテキストボックス2つを配置しています。


「売上明細」サブフォームでは「伝票番号」と「削除」フィールドを非表示にして、以下の既定値を設定しました。

PostgreSQLのテーブルから「売上伝票」と「売上明細」を取得するコードの記述
標準モジュールにPostgreSQLのテーブルから売上伝票と売上明細を取得する関数「wtINSERT」を記述しました。
Public Const strCN As String = "Driver={PostgreSQL Unicode};Server=localhost;Port=5432;
DATABASE=sample;Uid=postgres;Pwd=1111"
Public Function wtINSERT(ByVal strWT As String, ByVal strSQL As String) As Boolean
On Error GoTo Errh
DoCmd.SetWarnings False
Dim db As Database
Set db = CurrentDb
Dim qdf As QueryDef
Set qdf = db.CreateQueryDef("Q取込", "INSERT INTO " & strWT & " " & strSQL)
qdf.ODBCTimeout = 3
DoCmd.OpenQuery "Q取込"
DoCmd.SetWarnings True
db.QueryDefs.Delete "Q取込"
wtINSERT = True
Exit Function
Errh:
DoCmd.SetWarnings True
db.QueryDefs.Delete "Q取込"
wtINSERT = False
End Function
PostgreSQLの「T売上伝票」から目的のデータを削除するコードの記述
標準モジュールにPostgreSQLの「T売上伝票」から目的のデータを削除する関数「tDELETE」を作成しました。
Public Function tDELETE(ByVal intSlipNo As Integer) As Boolean
On Error GoTo Errh
Dim cn As New ADODB.Connection
cn.Open strCN
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandTimeout = 3
cmd.CommandType = adCmdText
cmd.CommandText = "DELETE FROM ""T売上伝票"" WHERE 伝票番号=" & intSlipNo
cmd.Execute
Set cmd = Nothing
cn.Close: Set cn = Nothing
tDELETE = True
Exit Function
Errh:
Set cmd = Nothing
cn.Close: Set cn = Nothing
tDELETE = False
End Function
PostgreSQLのストアドプロシージャを実行するコードの記述
標準モジュールにPostgreSQLのストアドプロシージャを実行する関数「ExecutePostgreSQLStoredProcedure」を作成しました。
Public Function ExecutePostgreSQLStoredProcedure(ByVal strStoredProcedure As String) As Boolean
On Error GoTo Errh
Dim cn As New ADODB.Connection
cn.Open strCN
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandType = adCmdText
cmd.CommandText = "CALL " & strStoredProcedure
cmd.CommandTimeout = 3
cmd.Execute
If cn.Errors.Count = 0 Then
ExecutePostgreSQLStoredProcedure = True
Else
ExecutePostgreSQLStoredProcedure = False
End If
Set cmd = Nothing
cn.Close: Set cn = Nothing
Exit Function
Errh:
Set cmd = Nothing
cn.Close: Set cn = Nothing
End Function
PostgreSQLから主キー値を取得するコードの記述
標準モジュールにPostgreSQLから主キー値を取得する関数「GetID」を作成しました。
Public Function GetID(ByRef n As Integer) As Boolean
On Error GoTo Errh
DoCmd.SetWarnings False
Dim db As Database
Set db = CurrentDb
Dim qdf As QueryDef
For Each qdf In db.QueryDefs
If qdf.Name = "Q採番" Then
db.QueryDefs.Delete qdf.Name
End If
Next
Set qdf = db.CreateQueryDef()
With qdf
.Name = "Q採番"
.SQL = "select ""SetID""()"
.Connect = "ODBC;" & strCN
.ODBCTimeout = 3
End With
db.QueryDefs.Append qdf
db.QueryDefs.Refresh
Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset()
If rs.Fields(0).Value < 0 Then
GetID = False
MsgBox "エラーが発生しました。", vbExclamation, "確認"
Else
n = rs.Fields(0).Value
GetID = True
End If
db.QueryDefs.Delete "Q採番"
Set qdf = Nothing
rs.Close: Set rs = Nothing
db.Close: Set db = Nothing
Exit Function
Errh:
GetID = False
MsgBox "エラーが発生しました。", vbExclamation, "確認"
db.QueryDefs.Delete "Q採番"
Set qdf = Nothing
db.Close: Set db = Nothing
End Function
Accessのテーブルをクリアするコードの記述
標準モジュールにAccessのテーブルをクリアする関数「wtDELETE」を作成しました。
Public Function wtDELETE(ByVal strWT As String) As Boolean
On Error GoTo Errh
Dim w_cn As New ADODB.Connection
Set w_cn = CurrentProject.AccessConnection
Dim w_cmd As New ADODB.Command
w_cmd.ActiveConnection = w_cn
w_cmd.CommandType = adCmdText
w_cmd.CommandText = "DELETE FROM " & strWT
w_cmd.Execute
Set w_cmd = Nothing
w_cn.Close: Set w_cn = Nothing
wtDELETE = True
Exit Function
Errh:
Set w_cmd = Nothing
w_cn.Close: Set w_cn = Nothing
wtDELETE = False
End Function
PostgreSQLに一時テーブルを生成するコードの記述
標準モジュールにPostgreSQLに一時テーブルを生成する関数「tempEXPORT」を記述しました。
Public Function tempEXPORT(ByVal strWT As String, ByVal strSQL As String) As Boolean
On Error GoTo Errh
DoCmd.SetWarnings False
Dim db As Database
Set db = CurrentDb
Dim qdf As QueryDef
Set qdf = db.CreateQueryDef("Q書出", "SELECT * INTO " & strWT & " IN ''[ODBC;" & strCN & "] " & strSQL)
qdf.ODBCTimeout = 3
DoCmd.OpenQuery "Q書出"
DoCmd.SetWarnings True
db.QueryDefs.Delete "Q書出"
tempEXPORT = True
Exit Function
Errh:
DoCmd.SetWarnings True
db.QueryDefs.Delete "Q書出"
tempEXPORT = False
End Function
PostgreSQLの一時テーブル「temp売上伝票」にレコードを挿入するコードの記述
標準モジュールにPostgreSQLの一時テーブル「temp売上伝票」にレコードを挿入する関数「tempINSERT」を記述しました。
Public Function tempINSERT(ByVal strWT As String, ByVal strSQL As String) As Boolean
On Error GoTo Errh
DoCmd.SetWarnings False
Dim db As Database
Set db = CurrentDb
Dim qdf As QueryDef
Set qdf = db.CreateQueryDef("Q挿入", "INSERT INTO " & strWT & " IN ''[ODBC;" & strCN & "] " & strSQL)
qdf.ODBCTimeout = 3
DoCmd.OpenQuery "Q挿入"
DoCmd.SetWarnings True
db.QueryDefs.Delete "Q挿入"
tempINSERT = True
Exit Function
Errh:
DoCmd.SetWarnings True
db.QueryDefs.Delete "Q挿入"
tempINSERT = False
End Function
PostgreSQLの一時テーブルをクリアするコードの記述
標準モジュールにPostgreSQLの一時テーブルをクリアする関数「tempTRUNCATE」を作成しました。
Public Function tempTRUNCATE(ByVal strWT As String) As Boolean
On Error GoTo Errh
Dim cn As New ADODB.Connection
cn.Open strCN
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandTimeout = 3
cmd.CommandType = adCmdText
cmd.CommandText = "TRUNCATE """ & strWT & """"
cmd.Execute
Set cmd = Nothing
cn.Close: Set cn = Nothing
tempTRUNCATE = True
Exit Function
Errh:
Set cmd = Nothing
cn.Close: Set cn = Nothing
tempTRUNCATE = False
End Function
PostgreSQLの一時テーブルを削除するコードの記述
標準モジュールにPostgreSQLの一時テーブルを削除する関数「tempDROP」を作成しました。
Public Function tempDROP(ByVal strWT As String) As Boolean
On Error GoTo Errh
Dim cn As New ADODB.Connection
cn.Open strCN
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandTimeout = 3
cmd.CommandType = adCmdText
cmd.CommandText = "DROP TABLE IF EXISTS """ & strWT & """"
cmd.Execute
Set cmd = Nothing
cn.Close: Set cn = Nothing
tempDROP = True
Exit Function
Errh:
Set cmd = Nothing
cn.Close: Set cn = Nothing
tempDROP = False
End Functionフォーム用プロシージャの記述
「Fサンプル」の読み込み時と、「新規作成」ボタンおよび「保存」ボタンのクリック時のイベントプロシージャに以下のコードを記述しました。
Private Sub Form_Load()
On Error GoTo Errh
Dim strSQL As String
Importh:
If wtDELETE("WT売上伝票") = False Then GoTo Errh
If wtDELETE("WT売上明細") = False Then GoTo Errh
strSQL = "SELECT * FROM T売上伝票 IN ''[ODBC;" & strCN & "]"
If wtINSERT("WT売上伝票", strSQL) = False Then GoTo Errh
Me.sub伝票一覧.Requery
If DCount("*", "WT売上伝票") = 0 Then Exit Sub
[伝票番号] = Me.sub伝票一覧.Form![伝票番号]
[日付] = Me.sub伝票一覧.Form![日付]
strSQL = "SELECT * FROM T売上明細 IN ''[ODBC;" & strCN & "] WHERE 伝票番号=" & [伝票番号]
If wtINSERT("WT売上明細", strSQL) = False Then GoTo Errh
Me.sub売上明細.Requery
Exit Sub
Errh:
Dim msg As String
Dim res As Integer
msg = "エラーが発生しました。" & vbCrLf & "もう一度読み込みますか?"
res = MsgBox(msg, vbYesNo + vbExclamation, "確認")
If res = vbNo Then
DoCmd.Close acForm, Me.Name
Else
GoTo Importh
End If
End Sub
'「新規作成」ボタンクリック時のプロシージャ--------------------------------------------
Private Sub btnNew_Click()
Dim n As Integer
If GetID(n) Then
[伝票番号] = n
Else
[伝票番号] = Null
End If
Call wtDELETE("WT売上明細")
[日付] = Null
Me.sub売上明細.SourceObject = "F売上明細"
End Sub
'「保存」ボタンクリック時のプロシージャ-------------------------------------------------
Private Sub btnUpdate_Click()
If IsNull([伝票番号]) Then Exit Sub
'売上明細ゼロ件の時、売上伝票を削除する-----------------------------------
Deleteh:
If DCount("*", "Q売上明細") = 0 Then
If tDELETE([伝票番号]) = False Then GoTo Errh
[伝票番号] = Null
[日付] = Null
MsgBox "保存しました。", vbInformation, "確認"
GoTo UD:
End If
'---------------------------------------------------------------------------
On Error GoTo Errh
'PostgreSQLの一時テーブルにAccessのデータを転記する--------------------------
Dim strSQL As String
If tempDROP("TEMP売上伝票") = False Then GoTo Errh
If tempDROP("TEMP売上明細") = False Then GoTo Errh
strSQL = "FROM WT売上伝票"
If tempEXPORT("TEMP売上伝票", strSQL) = False Then GoTo Errh
If tempTRUNCATE("TEMP売上伝票") = False Then GoTo Errh
strSQL = "VALUES(" & [伝票番号]
strSQL = strSQL & ",'" & [日付]
strSQL = strSQL & "')"
If tempINSERT("TEMP売上伝票", strSQL) = False Then GoTo Errh
strSQL = "FROM WT売上明細"
If tempEXPORT("TEMP売上明細", strSQL) = False Then GoTo Errh
On Error GoTo 0
'---------------------------------------------------------------------------
'PostgreSQLの一時テーブルから生データテーブルに転記する----------------------
If ExecutePostgreSQLStoredProcedure("import売上情報()") = True Then
MsgBox "保存しました。", vbInformation, "確認"
If tempDROP("TEMP売上伝票") = False Then GoTo Errh
If tempDROP("TEMP売上明細") = False Then GoTo Errh
Else
GoTo Errh
End If
'---------------------------------------------------------------------------
UD:
Me.sub伝票一覧.Form.Painting = False
If wtDELETE("WT売上伝票") = False Then GoTo Errh
strSQL = "SELECT * FROM T売上伝票 IN ''[ODBC;" & strCN & "]"
If wtINSERT("WT売上伝票", strSQL) = False Then GoTo Errh
Me.sub伝票一覧.Requery
If Not IsNull([伝票番号]) Then
Me.sub伝票一覧.Form.Recordset.MoveFirst
Me.sub伝票一覧.Form.Recordset.FindFirst "伝票番号=" & [伝票番号]
End If
Me.sub伝票一覧.Form.Painting = True
If DCount("*", "WT売上伝票") <> 0 Then
[伝票番号] = Me.sub伝票一覧.Form.[伝票番号]
[日付] = Me.sub伝票一覧.Form.[日付]
If wtDELETE("WT売上明細") = False Then GoTo Errh
strSQL = "SELECT * FROM T売上明細 IN ''[ODBC;" & strCN & "] WHERE 伝票番号=" & [伝票番号]
If wtINSERT("WT売上明細", strSQL) = False Then GoTo Errh
Me.sub売上明細.Requery
End If
Exit Sub
Errh:
Dim msg As String
Dim res As Integer
msg = "エラーが発生しました。" & vbCrLf & "もう一度保存しますか?"
res = MsgBox(msg, vbYesNoCancel + vbExclamation, "確認")
If res = vbNo Then
GoTo UD
ElseIf res = vbYes Then
GoTo Deleteh
Else
DoCmd.Close acForm, Me.Name
End If
End Subサブフォーム用プロシージャの記述
「F伝票一覧」のクリック時に以下のイベントプロシージャを記述しました。
Public Sub Form_Click()
Importh:
If wtDELETE("WT売上明細") = False Then GoTo Errh
Forms![Fサンプル].[伝票番号] = [伝票番号]
Forms![Fサンプル].[日付] = [日付]
Dim strSQL As String
strSQL = "SELECT * FROM T売上明細 IN ''[ODBC;" & strCN & "] WHERE 伝票番号=" & [伝票番号]
If wtINSERT("WT売上明細", strSQL) = False Then GoTo Errh
Forms![Fサンプル].sub売上明細.Form.Painting = False
Forms![Fサンプル].sub売上明細.Requery
Forms![Fサンプル].sub売上明細.Form.Painting = True
Exit Sub
Errh:
Dim msg As String
Dim res As Integer
msg = "エラーが発生しました。" & vbCrLf & "もう一度読み込みますか?"
res = MsgBox(msg, vbYesNo + vbExclamation, "確認")
If res = vbNo Then
DoCmd.Close acForm, Me.Name
Else
GoTo Importh
End If
End Sub「F売上明細」の「削除」ボタンのクリック時に以下のイベントプロシージャを記述しました。
Private Sub btnDelete_Click()
If Me.NewRecord Then
MsgBox "新規レコードは削除できません。"
Exit Sub
End If
[削除] = True
Me.Requery
End Sub

