VBA

<実例>データベースを集計する方法

記事内に商品プロモーションを含む場合があります
このページでわかること

データベースから集計プログラムを作ったので紹介します。

こんにちは、hokkyokunです。
実務で作ってほしいと言われたプログラムを紹介します。
基本的な構文とエクセルのシート関数を理解していると結構コード少なく作れるので
何かの参考にしてもらえたらと思います。

データ

コード

Sub monthly_Totalization()
    Dim table As ListObject
    Dim dicts_Lists As New Dictionary
    Dim r As Range
    Dim ws As Worksheet
    Dim input1, input2 As String
    Dim input_Date1, input_Date2, date_ As Date
    Dim date_Span, date_Spans() As Variant
    Dim i, j As Long
    Dim l As Variant
    Dim first_day, last_day As Date
    
    'インプットボックスで日付を入力する。
    '必要なのは年と月なので日は1日にする。
    input1 = InputBox("yyyymm型で入力してください")
    If Len(input1) <> 6 Then
        MsgBox ("最初から入力してください")
        Exit Sub
    Else
        input_Date1 = CDate(Left(input1, 4) & "/" & Mid(input1, 5, 2) & "/" & "01")
    End If
    
    
    input2 = InputBox("yyyymm型で入力してください")
    If Len(input2) <> 6 Then
        MsgBox ("最初から入力してください")
        Exit Sub
    Else
        input_Date2 = CDate(Left(input2, 4) & "/" & Mid(input2, 5, 2) & "/" & "01")
    End If
    
    date_ = input_Date1
    Do While date_ <= input_Date2
        ReDim Preserve date_Spans(i)
        date_Spans(i) = date_
        date_ = DateAdd("m", 1, date_)
        i = i + 1
    Loop
    
    
    Set table = ThisWorkbook.Worksheets(1).ListObjects(1)
    
    'ユニークリストの作成
    For Each r In table.ListColumns("部門").DataBodyRange
        On Error Resume Next
        dicts_Lists.Add r.Value, r.Value
        On Error GoTo 0
    Next

    'アウトプット用のワークシート作成(ダブらないように前回のシートを消す)
    For Each ws In ThisWorkbook.Worksheets
        If ws.Name = "部門別検体数" Then
            Application.DisplayAlerts = False
            ws.Delete
            Application.DisplayAlerts = True
        End If
    Next
    Worksheets.Add after:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count)
    Set ws = ActiveSheet
    ws.Name = "部門別検体数"
    
    '表の横軸(月別)入力
    i = 2
    For Each date_Span In date_Spans
        ws.Cells(1, i).Value = Format(date_Span, "yyyy/mm")
        i = i + 1
    Next
    
    '表の縦軸(部門別)を入力しながら、検体数をカウントしていく。
    i = 2
    For Each l In dicts_Lists
        ws.Cells(i, 1).Value = l
        j = 2
        For Each date_Span In date_Spans
            first_day = date_Span
            last_day = DateAdd("d", -1, DateAdd("m", 1, date_Span))
            ws.Cells(i, j).Value = Application.WorksheetFunction.CountIfs( _
                                                    table.ListColumns("試験日").DataBodyRange, ">=" & first_day, _
                                                    table.ListColumns("試験日").DataBodyRange, "<=" & last_day, _
                                                    table.ListColumns("部門").DataBodyRange, l)
            j = j + 1
        Next
        i = i + 1
    Next
End Sub

プログラムの流れ

なんか、長くなっちゃったけど、
やってることは結構基本的なことばかりです。
あと、ほかでも活用できる技術を使ってるつもりです。

  1. インプットボックスに日付(yyyymm形式つまり年と月)を二回(開始日と終了日)入力
  2. その二つの日付からタイムスパンを作成
  3. 辞書(Dictionary)にテーブル内のデータをユニークリストとして登録
  4. アウトプット用のシートを安全に作成

  5. 横軸作成
    表の縦軸作成と同時にテーブルから月別、部門別の検査数をカウント、転記。

インプットボックスに日付を入力

構文

InputBox(prompt, [ title ], [ default ], [ xpos ], [ ypos ], [ helpfile, context ])
 prompt:ダイアログボックスを出した時の文章(文字列)を指定。省略不可
 そのほか:基本的には必要ない。こだわりが出てきてからで十分。

ポイントをかいつまんで

  • インプットボックスの戻り値は文字列
    ⇒ 数値や日付はデータ型変換を忘れない
  • キャンセルの処理を忘れない
    ⇒ キャンセル時は長さ0の文字列
    ⇒ if 〇〇 = “” then みたいな処理を入れておく。
  • まじめにエラーキャッチをするなら、文字列の長さを制限したり、
    入力された文字が数値や日付に変換できるかどうかをチェック

このあたりは、別で記事かけたら書きますね!!!

二つの日付からタイムスパンを作成

二回目に入力した日付(終了日)になるまで
ひと月ごとの日付リストを配列に入れています。

例えば
インプットボックスに一回目(2021/5)と二回目(2021/8)を入力したら
[2021/5 , 2021/6 , 2021/7 , 2021/8]の配列を得ます。

Do loopを苦手とする人もいると思いますが、
もし苦手なら、Do WhileかDo untilかどっちでもいいので、
一方しか使わないで書くことをお勧めします。

もしこだわりがないなら、whileの方が書きやすいように思います。

日付の加算は

構文

DateAdd ( interval, number, date )
 interval : 追加する時間間隔。
 num : 加算する数。
 date : 元となる時間。これに時間間隔を追加したり、減算したりする。

  • 引数(interval) : よく使うのは yyyy:年 , m:月 , d:日
  • 引数(num) : 1なら1づつ追加、-1なら1づつ減算。
  • 月末も簡単に取れる。
    例:DateAdd(“d”,-1,#2021/5/1#)
     >>2021/4/30

テーブルからユニークリストを作成

Dictionaryの仕様として、
Keyに登録する値はすでに登録されている場合はエラーとなる
があります。

これを利用して、ユニークリストを作ることができます。

アウトプット用のシートを安全に作成

これも結構使います。

  • for文でワークブックのワークシート全部の名前を確認する
  • 該当の名前(今回は”部門別検体数”)があった場合はシートを消す。
  • そのままだと、「消していいですか?」というダイアログボックスがでるので
    Application.DisplayAlerts = False でアラート解除

エラーキャッチで処理する方法もありますが、
こっちの方が直感的に処理できるし、
エラー番号を調べなくていいというメリットはあります。

表作成

ちょっと複雑ですが、

  • 上で作ったタイムスパン(開始日から終了日までひと月ごとの日付)
    を横軸にセルを一つずつづらしながら転記
  • ユニークリスト(部門リスト)を縦軸にセルをずらしながら転記
    同時にワークシートファンクションのcountifsで特定のセルの数をカウント

構文

WorksheetFunction オブジェクト

エクセルシート状の優秀な関数を使用することができます。
今回はApplication.WorksheetFunction.CountIfsを使用します。

Application.WorksheetFunction.CountIfs ( “シート範囲1” , “条件1” , “シート範囲2” , “条件2” , ……)という感じです。

実行結果

プログラムを実行してみます。

下記のようにダイアログボックスが出現して日付を入力します。
これが開始日と終了日の二回出現します。

結果として下記のような
出力結果が出ます。
開始日と終了日を自由に設定すればいくらでも日付を追加できます。