集計モジュール


◎申し訳ありません
作成途上に放棄したものにつきなんの参考にもなりません

----------------------------------------------
Option Explicit
Option Base 1
Option Private Module


'汎用的に使える関数を記載する

'集計関係のモジュールを記述

'ステータスを格納する構造体を定義
'A:Alpabet N:Numeric
Private Type codeFormat
    GroupID As Integer       'グループの数
    Code As String * 10    	 'なんらかのコード(10文字の文字列とする)
    EXstat  As String * 1    '「存在ステータス」("Y" or "N")
    DPstat  As String * 2    '「重複状況」("OK" or "NG")
    Stotal  As Long          '「小計」(Num)
    Combined As String       '複合キー
End Type

Public Function CreateSummarySheet(Optional ByVal SmryShtName As String = SMRY_SHT_NAME, _
                                   Optional ByVal CtrlShtName As String = CTRL_SHT_NAME) As Boolean
'集計シートを作成する関数
    Dim strSheetName As String      '
    Dim bolFlg As Boolean
    Dim shtIndex As Integer
    Dim wksSheet As Worksheet

    'エラーハンドリング
    On Error GoTo Hdl_ERROR
   
    'シートがあったら削除する
    For Each wksSheet In ThisWorkbook.Worksheets
        If wksSheet.Name = SmryShtName Then
            Call DeleteSheet(SmryShtName, ThisWorkbook.Name)
            Exit For
        End If
    Next wksSheet
    
    '操作シートの次に新しいシートを挿入するためインデックスを取得
    For Each wksSheet In ThisWorkbook.Worksheets
        If wksSheet.Name = CtrlShtName Then
            shtIndex = wksSheet.Index
            Exit For
        End If
    Next wksSheet
    
    'シート作成が成功したらコマンドボタンを作成する
    If shtIndex <> 0 Then
        Set wksSheet = Sheets.Add(after:=Worksheets(shtIndex))
        wksSheet.Name = SmryShtName
        bolFlg = MakeCommandButton(wksSheet, 432.75, 14.25, 103.5, 22.5, "CMD_SUMMARY", "集計")
    Else
        CreateSummarySheet = False
        Exit Function
    End If
    
    '結果の判定
    If bolFlg <> False Then
        CreateSummarySheet = True
        Exit Function
    Else
        GoTo Hdl_ERROR
    End If
Hdl_ERROR:
    'シートを削除
    For Each wksSheet In ThisWorkbook.Worksheets
        If wksSheet.Name = SmryShtName Then
            Call DeleteSheet(SmryShtName, ThisWorkbook.Name)
            Exit For
        End If
    Next wksSheet
    Set wksSheet = Nothing
    CreateSummarySheet = False
End Function

Private Function MakeCommandButton(ByRef objTargetSheet As Worksheet, _
                                   ByVal siLeft As Single, _
                                   ByVal siTop As Single, _
                                   ByVal siWidth As Single, _
                                   ByVal siHeight As Single, _
                                   ByVal strBtnName As String, _
                                   ByVal strCaption As String) As Boolean
'指定シートの指定位置に指定名,指定キャプションでコマンドボタンを作成する
'※"MS Forms 2.0"をインクルード +
'[マクロ]->[セキュリティ]->[信頼のおける発行元]->[Visual Basic~]にチェック

    Dim strCode As String           '作成したシートに挿入するコード
    
    'オブジェクト操作準備
    objTargetSheet.Activate
    objTargetSheet.Range("A1").Activate
    
    'エラーハンドリング
    On Error GoTo Hdl_ERROR
    
    'コマンドボタン追加
    With objTargetSheet.OLEObjects.Add("Forms.CommandButton.1", Link:=False, _
        DisplayAsIcon:=False, Left:=siLeft, Top:=siTop, Width:=siWidth, Height:=siHeight)
        .Name = strBtnName
        .Object.Caption = strCaption
    End With
    Erase KsjData
    KsjData = GetKSJDataInMemory(KSJ_SHT_NAME)
    Call SetStatusInCode(Code_SHT_NAME, KSJ_SHT_NAME)
    
    'コード作成
    strCode = "Private Sub " & strBtnName & "_Click()" & vbCrLf
    strCode = strCode & "End Sub"
    
    'シートイベント追加
    With ThisWorkbook.VBProject.VBComponents(objTargetSheet.CodeName).CodeModule
'    'シートモジュールにコード挿入
        .InsertLines 1, strCode
    End With
    
    MakeCommandButton = True
    Exit Function
Hdl_ERROR:
    MsgBox "コマンドボタン生成中にエラーが起きました", vbCritical + vbOKOnly
    MakeCommandButton = False
End Function

Public Function CreateSummaryData(Optional ByVal CodeSheetName = Code_SHT_NAME, _
                             Optional ByVal SmrySheetName = SMRY_SHT_NAME) As Long
'集計シートのデータを作成する
'引数1:コードが記載されているシート名
'引数2:集計シートのシート名
'戻値:処理行数
    Dim wksCodeSheet As Worksheet   'コード記載シートのオブジェクト
    Dim wksSmrSheet As Worksheet    '集計シートのオブジェクト
    Dim objRange As Range           '合計用
    Dim Count As Long               'インデックスカウンタ
    Dim MinGid As Long              'グループIDの初期値
    Dim NowGID As Long              'グループIDのインデックス
    Dim MaxGID As Long              'グループIDの最大値
    Dim CodeRowBgn As Long          'コード設定シートのデータ開始行
    Dim CodeRowIdx As Long          'コードレベル設定シートのデータ現在行
    Dim CodeRowMax As Long          'コードレベル設定シートのデータ最終行
    Dim RowBgn As Long              '構造体の開始インデックス
    Dim RowIdx As Long              '構造体の現在インデックス
    Dim RowIdx2 As Long             '構造体の現在インデックス(子ループ用)
    Dim RowMax As Long              '構造体の最終インデックス
    Dim strWork1 As String          '文字列判定用のワーク変数
    Dim strWork2 As String          ' 〃
    Dim strBuf As String			'読み込みバッファ
    Dim strGID1 As String           '重複の判定用
    Dim strGID2 As String           ' 〃
    Dim DataStore() As codeFormat    'ステータスを格納する構造体
    Dim SmryStore() As codeFormat    'グループをユニークにしたものを格納する構造体
    
    '初期化
    Set wksCodeSheet = ThisWorkbook.Worksheets(Code_SHT_NAME)
    Set wksSmrSheet = ThisWorkbook.Worksheets(SMRY_SHT_NAME)
    CodeRowBgn = 2      '行ラベルの次から
    CodeRowMax = wksCodeSheet.UsedRange.Rows.Count
    RowBgn = 1
    MinGid = 1
    If CodeRowMax >= CodeRowBgn Then
        RowMax = CodeRowMax - CodeRowBgn + 1
    Else
        Err.Raise vbObjectError + 513, "MyProj.MyObject", _
        "データがありません"

    End If
    
    '集計シートをクリアして列の書式設定をを「文字列」にする
    wksSmrSheet.Activate
    wksSmrSheet.Cells.Select
    Selection.ClearContents
    wksSmrSheet.Range(Columns(1), Columns(6)).NumberFormatLocal = "@"  'A列~F列
    wksSmrSheet.Range("A1").Activate
    
    '集計シートにタイトル行の作成
    wksSmrSheet.Cells(1, 1).Value = "コード"
    wksSmrSheet.Cells(1, 2).Value = "存在ステータス"
    wksSmrSheet.Cells(1, 3).Value = "重複状況"
    wksSmrSheet.Cells(1, 4).Value = "小計"
    
    '構造体の動的宣言
    ReDim DataStore(RowMax)
    
    '構造体にコードデータをストア
    RowIdx = RowBgn
    For CodeRowIdx = CodeRowBgn To CodeRowMax Step 1
        With DataStore(RowIdx)
            .GroupID = 0    'グループID:とりあえず0埋め
            .DPstat = "OK"  '重複状態:とりあえず埋め
            .Stotal = 0     '小計:とりあえず埋め
            .EXstat = wksCodeSheet.Cells(CodeRowIdx, 5).Value  '存在ステータス
            .Code = wksCodeSheet.Cells(CodeRowIdx, 6).Value    '判定対象コード
            .Combined = .Code & .EXstat
            'ステータスが空だったら処理しないフラグをつける
            If Trim$(.EXstat) = "" Or Trim$(.Code) = "" Then
                .Combined = "Z"                                '"Z"は処理対象外をあらわす
            Else
                .Combined = Trim(.EXstat & .Code)    '複合キー
            End If
        End With
        RowIdx = RowIdx + 1
    Next
    
    '複合キー(コードと存在ステータスを連結したもの)をカウント
    Count = 1
    NowGID = 1
    MaxGID = NowGID
    For RowIdx = RowBgn To RowMax Step 1    '親ループ
        If RowIdx <> RowMax Then            '親ループの最終行以外で
            If DataStore(RowIdx).GroupID = 0 Then   'グループIDがセットされていないものが対象
                DataStore(RowIdx).GroupID = NowGID
                For RowIdx2 = RowBgn + 1 To RowMax Step 1   '子ループ
                    strWork1 = Trim(DataStore(RowIdx).Combined)
                    strWork2 = Trim(DataStore(RowIdx2).Combined)
                    If strWork1 = strWork2 Then     '複合キーが一致したらグループIDをセットし
                        DataStore(RowIdx2).GroupID = NowGID
                        Count = Count + 1           'カウンタを増加
                    End If
                    strWork1 = ""
                    strWork2 = ""
                Next
                If Count > 0 Then                   '処理済データがあったならば
                    'カウンタをセットする関数を記載する
                    Call SetMaxCountTocodeFormat(DataStore, NowGID, Count)
                End If
                MaxGID = NowGID                 'グループID最大値をセットし
                NowGID = NowGID + 1             'グループIDを進める
            End If
        Else                                '親ループの最終行で
            If DataStore(RowIdx).GroupID = 0 Then   'グループIDがセットされていないものに
                MaxGID = NowGID                     'グループID最大値をセットし
                DataStore(RowIdx).GroupID = NowGID  'グループIDをセットする
                DataStore(RowIdx).Stotal = 1        '当然カウンタは1になる
            End If
        End If
        Count = 0
    Next
    
    'グループIDでユニークにしたデータを格納する構造体を宣言
    ReDim SmryStore(MaxGID)
    
    'グループIDごとにデータをコピーする
    NowGID = MinGid
    Do
        For RowIdx = RowBgn To RowMax Step 1
            If DataStore(RowIdx).GroupID = NowGID Then
                With SmryStore(NowGID)
                    strWork1 = Trim(DataStore(RowIdx).Combined)
                    '対象外でないならデータをコピー
                    If strWork1 <> "Z" Then
                        .DPstat = DataStore(RowIdx).DPstat
                        .EXstat = DataStore(RowIdx).EXstat
                        .GroupID = DataStore(RowIdx).GroupID
                        .Code = DataStore(RowIdx).KSJcode
                        .Stotal = DataStore(RowIdx).Stotal
                        .Combined = "0"    '次の重複判定処理のためのフラグとして"0"で埋め
                    Else
                        .DPstat = ""
                        .EXstat = ""
                        .GroupID = DataStore(RowIdx).GroupID
                        .Combined = strWork1     '"Z"を書き込む
                    End If
                    strWork1 = ""
                End With
                NowGID = NowGID + 1
                Exit For
            End If
        Next
    Loop Until NowGID = MaxGID + 1
    
    '重複判定処理を行う(同じコードで違うGIDのものを重複とする)
    For NowGID = MinGid To MaxGID Step 1
        Count = 0   'カウンタ初期化
        If NowGID <> MaxGID Then    '最終行でない場合
            strWork1 = Trim(SmryStore(NowGID).Combined)
            strGID1 = SmryStore(NowGID).GroupID
            If strWork1 = "0" Then  '処理済でなかったならば子ループで重複を探す
                SmryStore(NowGID).Combined = "1"         '親を処理済にする
                strWork1 = SmryStore(NowGID).KSJcode    '比較元のコードをセット
                For RowIdx = NowGID + 1 To MaxGID Step 1 '子ループを進める
                    strWork2 = SmryStore(RowIdx).KSJcode
                    strGID2 = SmryStore(RowIdx).GroupID
                    If strWork2 = strWork1 Then          'コードが一致したら
                        If strGID2 <> strGID1 Then       '連結アクセス権の不一致を判定する
                            SmryStore(NowGID).DPstat = "NG" '
                            SmryStore(RowIdx).DPstat = "NG"
                        End If
                    End If
                Next
            End If
        Else                        '最終行
            strWork1 = Trim(SmryStore(NowGID).Combined)
            strGID1 = SmryStore(NowGID).GroupID
            If strWork1 = "0" Then
                SmryStore(NowGID).DPstat = "OK"
            End If
        End If
    Next
    
    'データの書き出し
    'A:コード'B:存在ステータス,C:重複状況,D:小計
    Count = 0
    For RowIdx = RowBgn To MaxGID Step 1
        'ステータスが対象外でなければ書き込む
        strWork1 = Trim$(SmryStore(RowIdx).Combined)
        If strWork1 <> "Z" Then
            With wksSmrSheet
                'タイトル行すっ飛ばしのため行は一つ増やして書き込む
                .Cells(RowIdx + 1, 1).Value = SmryStore(RowIdx).Code
                .Cells(RowIdx + 1, 3).Value = SmryStore(RowIdx).EXstat
                .Cells(RowIdx + 1, 4).Value = SmryStore(RowIdx).DPstat
                .Cells(RowIdx + 1, 5).Value = SmryStore(RowIdx).Stotal
                Count = Count + 1
            End With
        End If
    Next
    
    'ソートする
    wksSmrSheet.Range(Cells(1, 1), Cells(MaxGID + 1, 5)).Sort Key1:=Range("A2"), Order1:=xlAscending, Key2:=Range( _
        "E2"), Order2:=xlDescending, Key3:=Range("C2"), Order3:=xlDescending, _
        Header:=xlYes, OrderCustom:=1, MatchCase:=False, Orientation:= _
        xlTopToBottom, SortMethod:=xlPinYin, DataOption1:=xlSortTextAsNumbers, _
        DataOption2:=xlSortNormal, DataOption3:=xlSortNormal
        
    '合計を埋める
    Set objRange = wksSmrSheet.Range(Cells(2, 5), Cells(wksSmrSheet.UsedRange.Rows.Count, 5))
    wksSmrSheet.Cells(2, 6).Value = WorksheetFunction.Sum(objRange)
    
    CreateSummaryData = Count

End Function

Private Function SetMaxCountTocodeFormat(ByRef DataStore() As codeFormat, _
                                        ByVal TargetGID As Long, _
                                        ByVal MaxCount As Long) As Boolean
'集計用構造体の指定GIDの小計全てに指定の数値を書き込む
'引数1:集計用構造体をアドレス渡し
'引数2:グループID
'引数3:セットする数値
'戻値:成功=TRUE,失敗=FALSE
    Dim BgnIdx As Long  '構造体の配列要素の開始値
    Dim CrtIdx As Long  '構造体の配列要素の現在値
    Dim MaxIdx As Long  '構造体の配列要素の最終値
    Dim Buf As Long     'ワーク用
    
    'エラーハンドリング
    On Error GoTo Hdl_ERROR
    
    '初期化
    BgnIdx = 1
    MaxIdx = UBound(DataStore)
                                        
    'データセット
    For CrtIdx = BgnIdx To MaxIdx Step 1
        Buf = DataStore(CrtIdx).GroupID
        If Buf = TargetGID Then
            DataStore(CrtIdx).Stotal = MaxCount
        End If
    Next
    SetMaxCountTocodeFormat = True
    Exit Function
Hdl_ERROR:
    SetMaxCountTocodeFormat = False
End Function