見出し画像

【VBA】動的配列を使って汎用的なプログラミングを書く

融通のきかないプログラム言語と言えば『VBA』

こんな台詞を何度耳にしたことでしょうか?

しかし、現場の実情を見ると業務管理システムとして重宝しまくっているのもVBAなんですよね。

今回は、動的配列を使って汎用的に書くVBAコーディングを紹介していきます。

何を配列にすれば便利なのか?

画像3

VBAを書いていく上で、配列にすれば便利になるオブジェクトはたくさんあります。

例えば「エクセルシート」もその一つ。

3つのシートにレコードセットを出力するコードを書く場合、シートを1つずつ指定しなければなりません。

Set xlSheet(0) =  xlBook.WorkSheets("sheet_a")
Set xlSheet(1) =  xlBook.WorkSheets("sheet_b")
Set xlSheet(2) =  xlBook.WorkSheets("sheet_c")

これを配列にすれば、同じファイルの中にあるシートなんだってイメージも付きやすいですよね。

これ以外にも、テーブルやクエリ等も配列として認識させることができます。

配列のポイントは、同じプログラム内で使われるパッケージのようなものを見つけること。

この考え方が、開発を進める上で便利になってくることもあります。

まずは固定長配列からおさらい

画像4

配列をググると固定長配列が目に付きます。

Dim str(2) As String

    str(0) = "a"
    str(1) = "b"
    str(2) = "c"

正直、この配列の形って使い道ないと思いませんか?

最初から変数が固定されているのであれば、配列にする意味って何だろう?と感じますよね。

1つ固定的な配列で便利な点をあげるとするならば、Forでぐるぐる回せる部分。

Dim str(2As String
Dim var As Variant
Dim msg As String

    msg = ""
    str(0) = "a"
    str(1) = "b"
    str(2) = "c"
    
    For Each var In str
        msg = msg & var
    Next var
    
    Msgbox msg 
    
    '//abc

こんな感じに、配列に代入した値をForで順番に回すことができます。

でもこれは最初から配列数が分かっている場合のみ…

配列がころころ変わるケースでは全く使えません。

動的に配列を使いたい場面

画像5

例えば、エクセルシートの数が日によって変わる場合はどうでしょうか?

3つの配列で固定してしまうと、毎回システム改修をしないといけなくなります。

動的配列は、そんなケースでも水のように柔軟な対応が可能になるので汎用性の高いシステムを構築したい場合にはとっても便利。

では、ここから動的配列のコーディング例を紹介していきましょう。

動的配列の元データをクエリで組む

画像6

ここでは例題として、エクセルシートの数がころころ変わる場面を想定したコードを紹介します。

出力するタイミングによってシートの数が1つになったり、6つになったりと統一性がない運用ってよくありますよね。

多くの場合は、最大のシート数をあらかじめ用意しておく運用にしていることでしょう。

しかし動的配列を使えば、必要な分だけスマートに出力することが可能になりますよ。

まずは宣言から

Dim xlApp As Object 'Excelアプリケーション'
Dim xlBook As Object 'Excelブック'
Dim xlSheet() As Object 'Excelシート' 

動的配列は宣言の段階で配列数が決まっていないので、カッコの中身は空白です。

さて、ここからがポイントです。

動的配列の配列数をどのデータから引っ張ってくるのか?

DBの設計段階からの話になりますが「何を基準にシート分けするのか?」が肝になってきます。

今回は、集計元テーブルの「categoryフィールド」を基準にシート作成するようにしましょう。

スクリーンショット 2020-09-06 15.35.30

例えば、こんな顧客データがあったとします。

categoryの種類は日によって増減し、固定的ではなかったとしましょう。

この顧客データをcategory毎に分けて、エクセルシートへする場合に動的配列が生きてきます。

まずは「categoryフィールド」に何種類の値が入っているのか?をクエリで抽出します。(テーブル名:t_顧客)

Select category From t_顧客 Group By category;

グループ化したcategoryデータのレコード数を配列数として格納する流れです。

スクリーンショット 2020-09-06 15.57.27

事前にクエリを組んでおけば、categoryが何種類になろうとも対応可能!

では、このレコードセットを使った想定でコーディングしていきましょう。

動的配列を使ったコーディング例

Dim rs(1As DAO.Recordset 'クエリのレコードセット'
Dim myFields As Field
Dim strSQL(1As String 'SQL'
Dim xlApp As Object 'Excelアプリケーション'
Dim xlBook As Object 'Excelブック'
Dim xlSheet() As Object 'Excelシート' 
Dim iArray(1As Integer '配列数'
Dim i
Dim strCategory As String
Dim iRow, iCol(1'行と列'
   
   
   strSQL(0) = "Select category From t_顧客 Group By category;"
   Set rs(0) = CurrentDB.OpenRecordset(strSQL(0))
   Set xlApp = CreateObject("Excel.Application")
       xlApp.Visible = True '可視化'
   Set xlBook = xlApp.Workbooks.Open("C:¥test.xlsx")
   
   With rs(0)
   
       .MoveLast '一旦最後まで行って'
       iArray(0) = .RecordCount - 1 '配列数取得/0始まりなので−1'
       .MoveFirst '最初に戻す'
       
       With xlBook
           .WorkSheets.Add after:=.WorkSheets(.WorkSheets.Count), _
                           Count:=iArray(0'シート追加'
       End With
       ReDim xlSheet(iArray(0)) 'ここで配列決定して再定義'
       
       i = -1
       Do Until .EOF
       
           i = i + 1
           Set xlSheet(i) = xlBook.WorkSheets(i + 1)
           strCategory = !category.Value
           strSQL(1) = "Select * From t_顧客 Where category =""" & _
                           strCategory & """"
                           
           Set rs(1) = CurrentDB.OpenRecordset(strSQL(1))
           With rs(1)
               iArray(1) = .Fields.Count - 1                
               iRow = 2
               iCol(0) = 1
               For Each myFields In .Fields'カラムを出力'
                   xlSheet(i).Cells(1, iCol(0)).Value = myFields.Name
                   iCol(0) = iCol(0) + 1
               Next myFields
               Do Until .EOF'データを出力'
                   For iCol(1) = 0 To iArray(1)
                       xlSheet(i).Cells(iRow,iCol(1) + 1).Value = .Fields(iCol(1)).Value
                   Next iCol(1)
                   .MoveNext
                   iRow = iRow + 1
               Loop
               .Close
           End With
           Set rs(1) = Nothing
           
           xlSheet(i).Name = "Sheet_" & strCategory'シート名変更'        
           .MoveNext
           
       Loop
       
       .Close
       
   End With

   xlBook.Close True’保存して閉じる’
   xlApp.Visible = False
  
   ‘解放’ 
   Erase iCol
   Erase iArray    
   Erase rs
   Erase strSQL
   Erase xlSheet
   Set xlBook = Nothing
   Set xlApp = Nothing

少し長くなりましたが、SQLでcategoryを抽出し動的配列へ格納したコード例です。

格納した配列数とシート数が同じになり、各シートへクエリの結果が出力される仕組みになります。

注意する点が、配列とエクセルオブジェクトのインデックスを同じにしないこと。

例えば、配列が0始まりなのに対して「xlBook.WorkSheets(Index)」のインデックスは1始まりです。

ここを押さえておかないと、たちまちエラーになってしまうので特に注意しておきましょう。

エクセルへの出力コードは、こちらの記事でも詳しく紹介されています。

ぜひ参考にしてみてください。


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