【Access VBA】SQL ServerのデータをUTF-8形式でCSVファイルに出力する

SQL ServerのデータをUTF-8形式でCSVファイルに出力する

SQL ServerのテーブルのデータをAccess経由でUTF-8形式のCSVファイルに出力します。

テーブルを準備する

SQL Serverで「sample」という名前のデータベースの中に「Tサンプル」という名前のテーブルを用意しました。

参照設定の準備

Visual Basic Editorを表示し、「ツール」タブの「参照設定」をクリック します。

Microsoft ActiveX Data Objects ×.× Library」をチェックし、「OK」ボタンをクリックします。

コードの記述

標準モジュールに以下のコードを記述しました。

Public Sub sample()
    Dim i As Integer
    Dim strCN As String
    strCN = "driver={ODBC Driver 17 for SQL Server};server=LAPTOP-114315\SQLEXPRESS;DATABASE=sample;uid=admin;PWD=1111"
        
    Dim cn As ADODB.Connection
    Set cn = CurrentProject.Connection

    Dim rs As ADODB.Recordset
    Set rs = New ADODB.Recordset
    
    Dim strm As ADODB.Stream
    Set strm = New ADODB.Stream
  
    Dim vData As String
    Dim Target As String

    With rs
        .Open "SELECT * FROM Tサンプル IN ''[ODBC;" & strCN & "]", cn, adOpenForwardOnly, adLockReadOnly, adCmdText
            For i = 0 To rs.Fields.Count - 1
                vData = vData & .Fields(i).Name
                If i < rs.Fields.Count - 1 Then vData = vData & ","
            Next
            vData = vData & vbCrLf

        Do Until .EOF
            For i = 0 To rs.Fields.Count - 1
                vData = vData & .Fields(i).Value
                If i < rs.Fields.Count - 1 Then vData = vData & ","
            Next
            vData = vData & vbCrLf
            .MoveNext
        Loop
        .Close
    End With
    Set rs = Nothing
    cn.Close
    Set cn = Nothing
    
    Target = CurrentProject.Path & "\Sample.csv"
    
    With strm
          .Charset = "UTF-8"
          .Open
          .WriteText vData
          .SaveToFile Target, adSaveCreateOverWrite
          .Close
    End With
    Set strm = Nothing
End Sub

お名前.comのお名前メールプランとはてなブログを併用する

お名前.comのお名前メールプランとはてなブログを併用する

1.「お名前.com Navi」へログインします。
2.「ネームサーバー/DNS」→「ネームサーバー設定」をクリックします。

3.「1.ドメインの選択」にて、ネームサーバーの変更を行うドメインの左側にチェックを入れます。

4.「2.ネームサーバーの選択」にて「お名前.com」タブを選択し、使用するネームサーバーを選択して「確認」ボタンをクリックします。

5.確認画面が表示されますので、「OK」ボタンをクリックします。

6.「お名前メール」の「ログイン」ボタンをクリックして、コントロールパネルにログインします。

7.左メニューの「ドメイン」をクリックします。

8.「DNS」をクリックします。

9.「+DNSレコードを追加」ボタンをクリックします。

10.「タイプ」コンボボックスから「A」を選択します。
  「ホスト名」は空欄にしておきます。
  「TTL」に「3600」を入力します。
  「値」に「13.230.115.161」を入力します。
  「確認する」ボタンをクリックします。

11.「追加する」ボタンをクリックします。

12.「+DNSレコードを追加」ボタンをクリックします。

13.「タイプ」コンボボックスから「A」を選択します。
  「ホスト名」は空欄にしておきます。
  「TTL」に「3600」を入力します。
  「値」に「13.115.18.61」を入力します。
  「確認する」ボタンをクリックします。

14.「追加する」ボタンをクリックします。

15.一覧に入力したレコードが表示されます。

16.左メニューの「メール」をクリックします。

17.「+メールアドレスを追加」ボタンをクリックします。

※当該ドメインにてメールアドレスを作成したことがない場合は「+メールアドレスを追加」ボタンは表示されません。「はじめる」ボタンをクリックしてください。

18.「個別追加」を選択し、「情報入力する」ボタンをクリックします。

19.「メールアドレス」「パスワード」「パスワード(確認)」を入力して、「完了する」ボタンをクリックします。

20.「メールアドレスを追加しました!」の画面が表示されたら作成完了です。

【Access VBA】SQL ServerのデータをExcelに出力して罫線を引く

SQL ServerのデータをExcelに出力して罫線を引く

SQL ServerのテーブルのデータをAccess経由でExcelに出力して罫線を引きます。

テーブルを準備する

SQL Serverで「sample」という名前のデータベースの中に「Tサンプル」という名前のテーブルを用意しました。

SQL Serverにストアドプロシージャを準備する

SQL Serverに「exportサンプル」という名前のストアドプロシージャを用意しました。
これによりテンポラリテーブルにデータを書き込みますが、テンポラリテーブルに「通し番号」という名前の列を作成して日付ごとに通し番号を割り当てます。
この「通し番号」はExcelに出力したときに日付ごとにセルを塗りつぶす際に使用します。

CREATE PROCEDURE exportサンプル
	@ID int OUTPUT
AS
BEGIN
	
	SET NOCOUNT ON;

    BEGIN TRY	
		DECLARE @table_name varchar(50);
		DECLARE @sql varchar(1000);		
		SET @ID=@@SPID;  
		SET @table_name='';
		SET @table_name=@table_name+'##TEMPサンプル'+CONVERT(varchar,@@spid);
		SET @sql='';
		SET @sql=@sql+'IF EXISTS(SELECT * FROM tempdb.sys.tables WHERE name='''+@table_name+''')';
		SET @sql=@sql+'DROP TABLE '+@table_name+';';   
		SET @sql=@sql+'CREATE TABLE '+@table_name;
		SET @sql=@sql+'(';
		SET @sql=@sql+'明細番号 bigint';
		SET @sql=@sql+',日付 date';
		SET @sql=@sql+',商品コード varchar(10)';
		SET @sql=@sql+',商品名 varchar(20)';
		SET @sql=@sql+',数量 int';
		SET @sql=@sql+',通し番号 bigint';
		SET @sql=@sql+');';
		SET @sql=@sql+'INSERT INTO '+@table_name;
		SET @sql=@sql+' SELECT 明細番号,日付,商品コード,商品名,数量,';
		SET @sql=@sql+'DENSE_RANK() OVER(ORDER BY 日付) AS 通し番号';	
		SET @sql=@sql+' FROM Tサンプル ORDER BY 日付;';	
		EXECUTE(@sql);		 
		RETURN -1 
	END TRY

	BEGIN CATCH				
		RETURN 0
	END CATCH
END

SQL ServerのテンポラリテーブルのデータをExcelに出力して罫線を引くコードの記述

Accessの標準モジュールに以下のコードを記述しました。
「日付」,「商品コード」,「商品名」は1行上の値と同じ時はフォント色を背景色と同じにして空欄に見えるようにしました。
罫線、背景色、フォント色の設定には条件付き書式を使いました。

Public Sub sample()
    Dim strCN_sample As String
    strCN_sample = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=LAPTOP-114315\SQLEXPRESS;DATABASE=sample;UID=admin;PWD=1111"
    Dim cn As New ADODB.Connection
    cn.CursorLocation = adUseClient
    cn.Open strCN_sample
    Dim sid As Integer 'SQL Serverが割り当てるセッションIDを格納する変数
    Dim cmd As New ADODB.Command
    cmd.ActiveConnection = cn
    cmd.CommandType = adCmdStoredProc
    cmd.CommandText = "exportサンプル"
    cmd.CommandTimeout = 3
    cmd.Parameters.Append cmd.CreateParameter("@return_value", adInteger, adParamReturnValue, , Null)
    cmd.Parameters.Append cmd.CreateParameter("@ID", adInteger, adParamOutput, , Null)
    cmd.execute
    
    If CBool(cmd.Parameters("@return_value").Value) = False Then GoTo Errh
    sid = cmd.Parameters("@ID").Value
    Dim rs As New ADODB.Recordset
    rs.Open "SELECT * FROM [##TEMPサンプル" & CStr(sid) & "]", cn, adOpenStatic, adLockReadOnly
    Dim xlApp As Object
    Dim wb As Object
    Dim ws As Object
    Dim file_name As String
    Dim i As Integer
    file_name = CurrentProject.Path & "\sample.xlsx"
    Set xlApp = CreateObject("Excel.Application")
    Set wb = xlApp.Workbooks.Add
    Set ws = wb.Worksheets(1)
    
    rs.MoveLast
    Dim startRow As Integer
    startRow = 2
    Dim lastRow As Long
    lastRow = rs.RecordCount + 1
    Dim lastColumn As Integer
    lastColumn = 5

    For i = 0 To 4
        ws.Cells(1, i + 1) = rs.Fields(i).Name
    Next
    rs.MoveFirst
    ws.Range("A2").CopyFromRecordset rs
    With ws.Range(ws.Cells(1, 1), ws.Cells(lastRow, lastColumn))
        .Borders(xlEdgeTop).Weight = xlThick
        .Borders(xlEdgeLeft).Weight = xlThick
        .Borders(xlEdgeRight).Weight = xlThick
        .Borders(xlEdgeBottom).Weight = xlThick
        .Borders(xlInsideVertical).LineStyle = xlContinuous
    End With
    With ws.Range(ws.Cells(startRow, 1), ws.Cells(lastRow, 1))
        .FormatConditions.Delete
        .FormatConditions.Add Type:=xlExpression, Formula1:="=R[0]C2<>R[-1]C2"
        .FormatConditions(1).Borders(xlTop).LineStyle = xlContinuous
        .FormatConditions(1).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=R[0]C2=R[-1]C2"
        .FormatConditions(2).Borders(xlTop).LineStyle = xlDot
        .FormatConditions(2).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=MOD(R[0]C6,2)=1"
        .FormatConditions(3).Interior.Color = vbYellow
        .FormatConditions(3).StopIfTrue = False
    End With
    With ws.Range(ws.Cells(startRow, 2), ws.Cells(lastRow, 2))
        .FormatConditions.Delete
        .FormatConditions.Add Type:=xlExpression, Formula1:="=R[-1]C[0]<>R[0]C[0]"
        .FormatConditions(1).Borders(xlTop).LineStyle = xlContinuous
        .FormatConditions(1).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=MOD(R[0]C6,2)=1"
        .FormatConditions(2).Interior.Color = vbYellow
        .FormatConditions(2).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=AND(R[-1]C[0]=R[0]C[0],MOD(R[0]C6,2)=0)"
        .FormatConditions(3).Font.Color = vbWhite
        .FormatConditions(3).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=AND(R[-1]C[0]=R[0]C[0],MOD(R[0]C6,2)=1)"
        .FormatConditions(4).Font.Color = vbYellow
        .FormatConditions(4).StopIfTrue = False
    End With
    With ws.Range(ws.Cells(startRow, 3), ws.Cells(lastRow, 4))
        .FormatConditions.Delete
        .FormatConditions.Add Type:=xlExpression, Formula1:="=R[-1]C2<>R[0]C2"
        .FormatConditions(1).Borders(xlTop).LineStyle = xlContinuous
        .FormatConditions(1).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=AND(R[-1]C[0]<>R[0]C[0],R[-1]C2=RC2)"
        .FormatConditions(2).Borders(xlTop).LineStyle = xlDot
        .FormatConditions(2).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=MOD(R[0]C6,2)=1"
        .FormatConditions(3).Interior.Color = vbYellow
        .FormatConditions(3).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=AND(R[-1]C[0]=R[0]C[0],MOD(R[0]C6,2)=0)"
        .FormatConditions(4).Font.Color = RGB(255, 255, 255)
        .FormatConditions(4).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=AND(R[-1]C[0]=R[0]C[0],MOD(R[0]C6,2)=1)"
        .FormatConditions(5).Font.Color = vbYellow
        .FormatConditions(5).StopIfTrue = False
    End With
    With ws.Range(ws.Cells(startRow, 5), ws.Cells(lastRow, 5))
        .FormatConditions.Delete
        .FormatConditions.Add Type:=xlExpression, Formula1:="=R[0]C2<>R[-1]C2"
        .FormatConditions(1).Borders(xlTop).LineStyle = xlContinuous
        .FormatConditions(1).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=R[0]C2=R[-1]C2"
        .FormatConditions(2).Borders(xlTop).LineStyle = xlDot
        .FormatConditions(2).StopIfTrue = False
        .FormatConditions.Add Type:=xlExpression, Formula1:="=MOD(R[0]C6,2)=1"
        .FormatConditions(3).Interior.Color = vbYellow
        .FormatConditions(3).StopIfTrue = False
    End With
    
    ws.Columns("A:E").AutoFit
    ws.Columns("F:F").EntireColumn.Hidden = True
    ws.Range("A:A").TextToColumns Destination:=ws.Range("A:A")
    ws.Range("C:C").TextToColumns Destination:=ws.Range("C:C")
    xlApp.DisplayAlerts = False
    wb.SaveAs file_name
    xlApp.DisplayAlerts = True
    xlApp.Quit
    Set xlApp = Nothing
    rs.Close: Set rs = Nothing
   
    Set cmd = Nothing
    cn.Close: Set cn = Nothing
    Exit Sub
Errh:
    MsgBox "エラーが発生しました。", vbExclamation, "確認"
    Set cmd = Nothing
    cn.Close: Set cn = Nothing
End Sub

【Access VBA】SQL Serverのテンポラリテーブルからデータを取得する

SQL Serverのテンポラリテーブルからデータを取得する

SQL Serverのテンポラリテーブルからデータを取得し、Accessのテーブルに取り込みます。
テンポラリテーブル名の末尾にセッションIDを付与して他のセッションが作成するテンポラリテーブルと重複しないようにします。

テーブルを準備する

SQL Serverで「sample」という名前のデータベースの中に「T売上明細」という名前のテーブルを用意しました。

Accessには「WT売上明細」という名前のテーブルを用意しました。テーブルにはデータが入力されていません。

SQL Serverにストアドプロシージャを準備する

SQL Serverに「export売上明細」という名前のストアドプロシージャを用意しました。
これによりテンポラリテーブルを作成し、入力パラメーターの@slip_noで受け取った伝票番号のレコードをテンポラリテーブルに書き込みます。

CREATE PROCEDURE export売上明細 	
	@slip_no int,@ID int OUTPUT
AS
BEGIN	
	SET NOCOUNT ON;

    BEGIN TRY			
		DECLARE @table_name VARCHAR(50);
		DECLARE @sql VARCHAR(1000);
		SET @ID=@@spid	   
		SET @table_name='';
		SET @table_name=@table_name+'##TEMP売上明細'+CONVERT(VARCHAR,@@spid);
		SET @sql='';
		SET @sql=@sql+'IF EXISTS(SELECT * FROM tempdb.sys.tables WHERE name='''+@table_name+''')';
		SET @sql=@sql+'DROP TABLE '+@table_name+';';   
		SET @sql=@sql+'CREATE TABLE '+@table_name;
		SET @sql=@sql+'(';
		SET @sql=@sql+'明細ID int';
		SET @sql=@sql+',伝票番号 int';
		SET @sql=@sql+',商品コード VARCHAR(4)';
		SET @sql=@sql+',数量 int';
		SET @sql=@sql+');';
		SET @sql=@sql+'INSERT INTO '+@table_name;
		SET @sql=@sql+' SELECT * FROM T売上明細 WHERE 伝票番号=';
		SET @sql=@sql+ CONVERT(VARCHAR,@slip_no)
                EXECUTE(@sql);	 
		RETURN -1 
	END TRY

	BEGIN CATCH				
		RETURN 0
	END CATCH
END

SQL ServerのテンポラリテーブルのデータをAccessのテーブルに取得するコードの記述

Accessの標準モジュールに以下のコードを記述しました。

Public Sub ExecuteStoredProcedure()
    On Error GoTo ErrhA
    Dim strCN_sample As String
    strCN_sample = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=LAPTOP-114315\SQLEXPRESS;DATABASE=sample;UID=admin;PWD=1111"
    Dim cn As New ADODB.Connection
    cn.Open strCN_sample
    Dim sid As Integer 'SQL Serverが割り当てるセッションIDを格納する変数
    Dim slip_no As Integer 'Accessに書き出したい伝票番号を格納する変数
    slip_no = 522
    Dim cmd As New ADODB.Command
    cmd.ActiveConnection = cn
    cmd.CommandType = adCmdStoredProc
    cmd.CommandText = "export売上明細"
    cmd.CommandTimeout = 3
    cmd.Parameters.Append cmd.CreateParameter("@return_value", adInteger, adParamReturnValue, , Null)
    cmd.Parameters.Append cmd.CreateParameter("@slip_no", adInteger, adParamInput, , slip_no)
    cmd.Parameters.Append cmd.CreateParameter("@ID", adInteger, adParamOutput, , Null)
    cmd.Execute
    On Error GoTo 0
    
    If CBool(cmd.Parameters("@return_value").Value) Then
        On Error GoTo ErrhB
        sid = cmd.Parameters("@ID").Value
        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
        Dim strCN_temp As String
        strCN_temp = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=LAPTOP-114315\SQLEXPRESS;DATABASE=tempdb;UID=admin;PWD=1111"
        w_cmd.CommandText = "INSERT INTO WT売上明細 SELECT * FROM [##TEMP売上明細" & CStr(sid) & "] IN ''[ODBC;" & strCN_temp & "]"
        w_cmd.Execute
        On Error GoTo 0
        Set w_cmd = Nothing
        w_cn.Close: Set w_cn = Nothing
    Else
        MsgBox "エラーが発生しました。", vbExclamation, "確認"
    End If
    Set cmd = Nothing
    cn.Close: Set cn = Nothing
    Exit Sub
ErrhA:
    MsgBox "エラーが発生しました。", vbExclamation, "確認"
    Set cmd = Nothing
    cn.Close: Set cn = Nothing
    Exit Sub
ErrhB:
    MsgBox "エラーが発生しました。", vbExclamation, "確認"
    Set w_cmd = Nothing
    w_cn.Close: Set w_cn = Nothing
End Sub

動作確認

AccessのExecuteStoredProcedureプロシージャの中のslip_noに指定した伝票番号のレコードがAccessのテーブルに抽出されました。

【Access VBA】SQL ServerのテーブルをPostgreSQLにコピーする


SQL ServerのテーブルをPostgreSQLにコピーする

SQL ServerのテーブルのデータをAccess経由でPostgreSQLのテーブルにコピーします。

テーブルを準備する

SQL Serverで「sample」という名前のデータベースの中に「Tサンプル」という名前のテーブルを用意しました。このテーブルにはあらかじめデータが入力されているものとします。

PostgreSQLで「sample」という名前のデータベースの中に「Tサンプル」という名前のテーブルを用意しました。このテーブルはSQL Serverのテーブルと構造は同じですが、データは入力されていません。

SQL ServerのテーブルのデータをPostgreSQLのテーブルにコピーするコードの記述

Accessの標準モジュールに以下のコードを記述しました。
SQL ServerのテーブルのコピーをAccessに作成し、AccessのテーブルからPostgreSQLのテーブルにデータを追加したのちAccessのテーブルは削除します。

Public Sub COPY_TABLE()
    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
'コピー元
    Dim strCN_source As String
    strCN_source = "driver={ODBC Driver 17 for SQL Server};server=LAPTOP-114315\SQLEXPRESS;DATABASE=sample;uid=administrator;PWD=1111"
    w_cmd.CommandText = "SELECT * INTO " & "TARGET" & " " & "FROM Tサンプル AS A IN ''[ODBC;" & strCN_source & "]"
    w_cmd.Execute
'コピー先
    Dim strCN_destination As String
    strCN_destination = "Driver={PostgreSQL Unicode};server=localhost;DATABASE=sample;port=5432;UID=postgres;PWD=1111"

    Dim tableName_destination As String
    tableName_destination = "Tサンプル"
    w_cmd.CommandText = "INSERT INTO " & tableName_destination & " IN ''[ODBC;" & strCN_destination & "] SELECT * FROM TARGET;"
    w_cmd.Execute
    DoCmd.DeleteObject acTable, "TARGET"
    Set w_cmd = Nothing
    w_cn.Close: Set w_cn = Nothing
End Sub

動作確認

PostgreSQLのテーブルにデータがコピーされました。

【Access VBA】PostgreSQLのテーブルを編集する

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;
$$

選択クエリの作成

Accessで「Q売上明細」という名前の選択クエリを作成しました。サブフォームのレコードソースとして使用します。

フォームの準備

下のような「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

【Access VBA】カレンダーコントロールの作成(機能追加)

カレンダーコントロールの作成(機能追加)

下記リンク先にて作成したカレンダーコントロールの「年」テキストボックスをクリックすると、20年分の「年」一覧が表示され、目的の年をクリックすると「年」テキストボックスに入力されるようにします。スピンボタンをクリックすると前の20年分あるいは次の20年分が表示されます。
【Access VBA】カレンダーコントロールの作成 - カットマンブログ


ラベルコントロールの作成

「年」一覧のラベルを生成するために下記コードを標準モジュールに記述し実行します。宣言セクションのPublic変数は「年」一覧かカレンダーのどちらが表示されているかを判定するために使用します。

Public boolFlag As Boolean '年一覧:TRUE,カレンダー:FALSE
Private Sub ラベルコントロール作成_年ラベル()
    DoCmd.OpenForm "Fカレンダー", acDesign
    Dim lbl As Control
    Dim i As Integer, j As Integer, c As Integer
    Const leftMgn As Single = 0.2 '左余白(cm)
    Const rightMgn As Single = 0.2 '右余白(cm)
    Const topMgn As Single = 1.2 '上余白(cm)
    Const bottomMgn As Single = 0.2 '下余白(cm)
    Const lblMgn As Single = 0.1 'ラベル間隔(cm)
    Const lblWidth As Single = 2.46 'ラベル幅(cm)
    Const lblHeight As Single = 1 'ラベル高さ(cm)
    Const cmTwip As Single = 567 '1cm当たりのtwip
    
    c = 1
    For i = 1 To 7
        For j = 1 To 3
            Set lbl = CreateControl("Fカレンダー", acLabel, , , , cmTwip * leftMgn + _
                      cmTwip * (lblWidth + lblMgn) * (j - 1), cmTwip * topMgn _
                       + (i - 1) * cmTwip * (lblHeight + lblMgn), _
                      cmTwip * lblWidth, cmTwip * lblHeight)
            With lbl
                .BorderColor = vbBlack
                .BorderStyle = 1
                .ForeColor = vbBlack
                .FontSize = 22
                .TextAlign = 2
                .TopMargin = 50
                .Name = "年" & c
                c = c + 1
            End With
        Next
    Next
    DoCmd.Close acForm, "Fカレンダー", acSaveYes
End Sub


「年」テキストボックスをクリックしたときの処理の記述

変数boolFlagがTRUEの場合は「年」一覧を表示し、FALSEの場合はカレンダーを表示します。

Private Sub txtYear_Click()
    Dim i As Integer
    If boolFlag Then
        For i = 1 To 7
            Controls("曜日" & i).Visible = True
        Next
        For i = 1 To 42
            Controls("日" & i).Visible = True
        Next
        For i = 1 To 21
            Controls("年" & i).Visible = False
        Next
        boolFlag = False
    Else
        For i = 1 To 7
            Controls("曜日" & i).Visible = False
        Next
        For i = 1 To 42
            Controls("日" & i).Visible = False
        Next
        For i = 1 To 21
            Controls("年" & i).Visible = True
            Controls("年" & i).Caption = Int(Val(txtYear.Value) / 10) * 10 + i - 1 & "年"
            Controls("年" & i).Tag = Int(Val(txtYear.Value) / 10) * 10 + i - 1 & "年"
        Next
        boolFlag = True
    End If
End Sub


年ラベルをクリックしたときの処理の記述

クラスモジュールにclsYearを作成し、下記のコードを記述します。

Private WithEvents mLbl As Label
Private mForm As Form
 
Public Sub Bind(ByVal oCtrl As Control, ByVal oForm As Form)
    Set mLbl = oCtrl
    Set mForm = oForm
    mLbl.OnClick = "[EVENT PROCEDURE]"
End Sub
 
Private Sub mLbl_Click()
    mForm.Form.Controls("txtYear") = mLbl.Tag
    Dim i As Integer
    For i = 1 To 7
        mForm.Form.Controls("曜日" & i).Visible = True
    Next
    For i = 1 To 42
        mForm.Form.Controls("日" & i).Visible = True
    Next
    For i = 1 To 21
        mForm.Form.Controls("年" & i).Visible = False
    Next
    Call mForm.displayLabel(Val(mForm.txtYear), Val(mForm.cboMonth))
    boolFlag = False
End Sub


スピンボタンをクリックしたときの処理の記述

「年」一覧が表示されているときに「年」スピンボタンをクリックすると表示が切り替わるようにコードを修正しました。

Private WithEvents mBtn As CommandButton
Private mTxt As TextBox
Private mCbo As ComboBox
Private mString As String
Private mForm As Form
Private startTime As Double
Private lngSpin As Long
Private mCtrl As Control

Public Sub Bind(ByVal oCtrl As Control, ByVal oForm As Form, ByVal oString As String, ByVal oCommandButton As CommandButton)
    Set mCtrl = oCtrl
    Set mForm = oForm
    mString = oString
    Set mBtn = oCommandButton
    mBtn.OnMouseDown = "[EVENT PROCEDURE]"
    mBtn.OnMouseUp = "[EVENT PROCEDURE]"
End Sub
 
Private Sub mBtn_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Dim i As Integer
    startTime = CDbl(Timer)
    Select Case TypeName(mCtrl)
    Case "TextBox"
        Set mTxt = mCtrl
        lngSpin = Val(mTxt.Value)
    Case "ComboBox"
        Set mCbo = mCtrl
        lngSpin = Val(mCbo.Value)
    End Select
    
    Do Until startTime = 0
                Select Case mBtn.Tag
                Case "Up"
                    lngSpin = lngSpin + 1
                Case "Down"
                    lngSpin = lngSpin - 1
                End Select
                
                If TypeName(mCtrl) = "TextBox" Then
                    Select Case boolFlag
                    Case True
                        Select Case mBtn.Tag
                        Case "Up"
                        For i = 1 To 21
                            mForm.Form.Controls("年" & i).Caption = Val(mForm.Form.Controls("年" & i).Tag) + 20 & "年"
                            mForm.Form.Controls("年" & i).Tag = mForm.Form.Controls("年" & i).Caption
                        Next
                        
                        Case "Down"                    
                        For i = 1 To 21
                            mForm.Form.Controls("年" & i).Caption = Val(mForm.Form.Controls("年" & i).Tag) - 20 & "年"
                            mForm.Form.Controls("年" & i).Tag = mForm.Form.Controls("年" & i).Caption
                        Next                        
                        End Select
                    
                    Case False
                    mTxt = lngSpin & mString
                    End Select
                Else                
                    If lngSpin > 12 Then
                        lngSpin = 1
                        mForm.txtYear = Val(mForm.txtYear) + 1 & "年"
                    End If
                    If lngSpin < 1 Then
                        lngSpin = 12
                        mForm.txtYear = Val(mForm.txtYear) - 1 & "年"
                    End If
                    mCbo = lngSpin & mString                
                End If
                Call mForm.displayLabel(Val(mForm.txtYear), Val(mForm.cboMonth))
                
                If CDbl(Timer) - startTime < 1 Then
                    Sleep (300)
                Else
                    Sleep (200)
                End If
            DoEvents
    Loop
    startTime = 0
End Sub
 
Private Sub mBtn_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    startTime = 0
End Sub


カレンダーフォーム用コードの記述

Fカレンダーの読み込み時のイベントプロシージャにコードを追加しました。
「年」ラベル用のクラスのインスタンスを生成し、変数 boolFlagをTrueに設定しました。

Private aClassCalendar() As New clsCalendar
Private aClassSpin(3) As New clsSpin
Private aClassYear() As New clsYear
Private Sub Form_Load()
    
    boolFlag = True
    txtYear_Click
    Dim i As Integer

    If sDate = 0 Then
        txtYear = Year(Date) & "年"
        cboMonth = Month(Date) & "月"
    Else
        txtYear = Year(sDate) & "年"
        cboMonth = Month(sDate) & "月"
    End If

    Call displayLabel(Val(txtYear), Val(cboMonth))

    For i = 1 To 12
        cboMonth.AddItem i & "月"
    Next
    For i = 1 To 42
        ReDim Preserve aClassCalendar(i)
        Call aClassCalendar(i).Bind(Controls("日" & i), Me)
    Next
    For i = 1 To 21
        ReDim Preserve aClassYear(i)
        Call aClassYear(i).Bind(Controls("年" & i), Me)
    Next
    Call aClassSpin(0).Bind(txtYear, Me, "年", btnUpYear)
    Call aClassSpin(1).Bind(txtYear, Me, "年", btnDownYear)
    Call aClassSpin(2).Bind(cboMonth, Me, "月", btnUpMonth)
    Call aClassSpin(3).Bind(cboMonth, Me, "月", btnDownMonth)
End Sub