見出し画像

【AccessとExcel】VBAでサブルーチンを使う方法とコツ

一連の処理をひとまとめにするサブルーチンを使いこなせば、書く内容がスマートになるし修正も入れやすいです。

しかし、どの部分をサブルーチンにすれば良いか判断がつかないことも多いはず。

今回は、コードをスマートにしたい人のためにサブルーチンを使う方法とコツを紹介します。

まずはサブルーチンについて「おさらい」

画像2

プログラムにおけるサブルーチンは、同じコードを書くのを避けるために使われます。

それだけでなく、プロシージャ名の付け方によっては後々改修する際に見つけやすいメリットがあり、コードの組み立て方としても理にかなっていると言えるでしょう。

少し例を紹介します。

Praivate Sub outputXl()

    If Dcount("*","クエリ1") = 0 Then
        MsgBox "データはありません。"
        Else
            DoCmd.TransferSpreadsheet acExport, 10, "クエリ1", "C:¥test.xlsx", True
            MsgBox "出力完了"
    End If
    
    If Dcount("*","クエリ2") = 0 Then
        MsgBox "データはありません。"
        Else
            DoCmd.TransferSpreadsheet acExport, 10, "クエリ2", "C:¥test.xlsx", True
            MsgBox "出力完了"
    End If
    
    If Dcount("*","クエリ3") = 0 Then
        MsgBox "データはありません。"
        Else
            DoCmd.TransferSpreadsheet acExport, 10, "クエリ3", "C:¥test.xlsx", True
            MsgBox "出力完了"    
    End If
    
    If Dcount("*","クエリ4") = 0 Then
        MsgBox "データはありません。"
        Else
            DoCmd.TransferSpreadsheet acExport, 10, "クエリ4", "C:¥test.xlsx", True
            MsgBox "出力完了"
    End If

    If Dcount("*","クエリ5") = 0 Then
        MsgBox "データはありません。"
        Else
            DoCmd.TransferSpreadsheet acExport, 10, "クエリ1", "C:¥test.xlsx", True
            MsgBox "出力完了"
    End If

End Sub

これをサブルーチン化すると以下のとおり

Private Sub outputXl()

    subRoutineOutputXl "クエリ1"
    subRoutineOutputXl "クエリ2"
    subRoutineOutputXl "クエリ3"
    subRoutineOutputXl "クエリ4"
    subRoutineOutputXl "クエリ5"        

End Sub

Private Sub subRoutineOutputXl(strQueryName As String)
    
    If Dcount("*",strQueryName) = 0 Then
        MsgBox "データはありません。"
        Else
            DoCmd.TransferSpreadsheet acExport, 10, strQueryName, "C:¥test.xlsx", True
            MsgBox "出力完了"
    End If
    
End Sub

単純なプログラムですが、これだけでもスッキリするしバグ対応もしやすいですよね。

これがシンプルなサブルーチンの例になります。

処理のレイヤーをイメージする

画像3

分かりやすい例で説明しましたが、実際VBAを組んでみると何をサブルーチン化すれば良いのか分からないことが多いです。

そこでイメージして欲しいのが処理のレイヤー。

処理の流れをイメージして、従軸になる部分と機能的な部分を切り離すことが重要になります。

例えば、あるテーブルのデータをExcelへ出力するケースで考えてみましょう。

出力したい元データはテーブルに格納されているので、主軸はテーブルオブジェクトもしくはレコードセットになるはずです。

一方でExcelへ書き出すのは、あくまで「機能」になるのでサブルーチン化の対象になります。

画像1

機能の部分を把握すれば、複数のテーブルからでも簡単に呼び出すことが可能です。

この考えで開発していくと、単純な親と子だけでなく更に単純な機能をサブルーチン化したプロシージャまで出来上がってきます。

これをクラスモジュールでパブリックにすれば、汎用性の高いプロシージャとして使えるでしょう。

渡す引数や戻り値の使い方にセンスを光らせる

サブルーチン化させるレイヤーと同時に考えたいのが、プロシージャに渡す引数や戻り値です。

前述の例だとクエリ名を引数として渡していますが、クエリによっては違う処理をさせたいケースもあるでしょう。

また処理が正しく実行されたか結果をFunctionで返すように作るのも、プログラマーがセンスを光らせる部分になります。

では、これらを踏まえて前述したコードを例に書いてみましょう。

Private Sub outputXl()
    
    Dim qd As DAO.QueryDef
    Dim sQueryName As String
    Dim bool As Boolean
  
        For Each qd In CurrentDB.QueryDefs
        
            sQueryName = qd.Name
            If sQueryName Like "クエリ*" Then '名前に「クエリ」が含まれていたら'
                bool = subRoutineOutputXl(sQueryName)
                Else '含まれていなければ(追加改修)'
                    bool = subRoutineOutputXl(sQueryName, "special")   
            End If
            
            Select Case bool
                Case True
                    MsgBox "出力完了"
                Case False
                    MsgBox "データはありません。"
            End Select
            
        Next qd

End Sub
    
'サブルーチンのファンクションプロシージャ'
Private Function subRoutineOutputXl(strQueryName As String, Optional strCategory As String = "normal") As Boolean
    
    Dim boo As Boolean
    Dim xlApp As Object, xlBook As Object, xlSheet As Object
    Dim qd As DAO.QueryDef, rs As DAO.Recordset
    Dim iRow, iCol
    Dim iField As Integer
    
        Select Case strCategory
        
            Case "normal"
    
                If Dcount("*",strQueryName) = 0 Then
                    boo = False '失敗'
                    Else
                        DoCmd.TransferSpreadsheet acExport, 10, strQueryName, "C:¥test.xlsx", True
                        boo = True '成功'
                End If
                
            Case "special"
            
                Set qd = CurrentDB.QueryDefs(strQueryName)
                Set rs = qd.OpenRecordset
                
                iRow = 1
                With rs
                
                    If .EOF Then
                        boo = False '失敗'
                        Else
                            Set xlApp = CreateObject("Excel.Application")
                                xlApp.Visible = True
                            Set xlBook = xlApp.Workbooks.Open("C:¥test.xlsx")
                            Set xlSheet = xlBook.Worksheets(1)
                            iField = .Fields.Count
                            Do Until .EOF
                                For iCol = 1 To iField
                                    xlSheet(iRow, iCol).Value = .Fields(iCol - 1).Value
                                Next iCol
                                .MoveNext
                                iRow = iRow + 1
                            Loop
                            boo = True '成功'
                            
                            xlBook.SaveAs "C:¥" & strQueryName & ".xlsx" '名前をつけて保存'
                            With xlApp
                                .WorkBooks(strQueryName & ".xlsx").Close
                                .Visible = False
                            End With
                            
                            Set xlSheet = Nothing
                            Set xlBook = Nothing
                            Set xlApp = Nothing                       
                    End If
                    
                    .Close    
                    
                End With
                
                Set qd = Nothing
                Set rs = Nothing
                
        End Select
        
        subRoutineOutputXl = boo '結果を返す'
    
End Function

少し長くなりましたが、処理をnormalとspecialでカテゴリ分したバージョンを書きました。

サブルーチンからの戻り値によって、メッセージボックスの内容を変更する仕組みです。

画像6

命名規則を作っておけばFor〜Eachを使ってオブジェクト名を検索して実行できるので、いちいちVBの中で名前を指定する必要がありません。

またspecialのカテゴリが追加改修されたものであれば、Optionalを積極的に使っていくのも一つの技法です。

頑張って美しいコードを目指そう!

画像4

今回紹介したのは、本当に一部分だけのコードですが僕が仕事で実際に書いているパターンになります。

重要なのはデータの流れをイメージして、根幹と枝葉の部分を把握すること。

それだけでもサブルーチン化する箇所が見えてくるし、途中で混乱してきても理にかなっていれば組み立てやすくなります。

仕事では直接的な評価をされにくい場所ではありますが、頑張って美しいコードを目指していきましょう。

画像6

DeepBlueでは、VBAに関する質問や要件定義の相談を受け付けています。

今回、この記事を読んで登録してくれた方には初回のみ無料でVBAマクロに関する相談を承りますよ。

興味ある方は、是非チェックしてみてくださいね。

この記事が気に入ったらサポートをしてみませんか?