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