[教えて!VBA] 第12回 各ワークシートのデータを集約して出力するにはどうすればいいの??
概要
この記事について
かんたんな概要と結論
サンプルを例示し、そのコードをベースにまとめました。
WorkSheets
コレクションの取り扱いと、集約後の加工についても少し触れています。
こんにちは、dedeです。
この記事では、
VBAマクロに関する質問のうち、
皆が疑問に思っているトピックについて解説いたします。
今回は、
ブックの各ワークシートのデータを集約して、他のワークシートやテキストファイルなどに出力する方法
を解説いたします。
※この記事は、Office VBAマクロのうち
Excel VBAマクロに関するトピックです。
レベル:初級者向け
環境
以下は、
Office 2016のExcel環境で検証済みです。
※2022/1時点の最新バージョンのExcelでも内容は変わりません。
ワークシートの情報にアクセス
ワークシートオブジェクトとは
各ワークシートの情報にアクセスするには、
ワークシートオブジェクトというオブジェクトを参照します。
VBAはVisual Basic 6.0の仕様に準拠しており、
オブジェクト指向のプログラミング言語です。
データは基本的にオブジェクト(データとそれに紐付く動作を一つにまとめたもの)として扱われます。
オブジェクトはクラスによって生成され、クラスはオブジェクトの雛形とみなすことができます。
詳しくは、こちらの記事で紹介しました。
ワークシートオブジェクトは、
ActiveSheet(表示中のワークシート)プロパティや、
WorkSheets コレクションを呼び出すことなどで参照できます。
WorkSheets コレクションについては下記で詳細に見ていきます。
1Sub ワークシートオブジェクトの情報にアクセスする()
2 Debug.Print ThisWorkbook.Worksheets(1).name '//ブックの一枚目のワークシートの名前を参照
3 Debug.Print ThisWorkbook.Worksheets(1).Range("B2").Value '//ブックの一枚目のワークシートの、B2セルに書き込まれた値を参照
4End Sub
ワークシートコレクションとは
ワークシートコレクションとは、
そのブックのワークシートがすべて格納されたコレクションを指します。
VBAにおけるコレクションとは、
同じ種類のオブジェクトやデータを一定の規則に従って格納し、引き出すことができるようにしたオブジェクトの集まりで、
それ自身もオブジェクトです。
「一定の規則」とは、
Collection.Item(インデックス番号)や Collection (インデックス番号)の形で〇〇番目のオブジェクトやデータを取得したり、
Collection.Count の形でコレクションに格納されたオブジェクトの数を算出したりする決まりを指します。
さらに For ループを用いて順番にオブジェクトのデータを処理できるなどの決まりも含まれます。
コレクションは、
New Collection の形で自分で定義できる方法もあれば、
WorkSheets のようにもともと定義されているものを即座に利用可能な場合も存在します。
1
2Sub コレクション利用サンプル()
3 Dim myColl As Collection '//自作のコレクションを宣言
4
5 Set myColl = New Collection '//自作のコレクションをインスタンス化(準備)
6
7 myColl.Add "りんご" '//自作のコレクションに文字列を格納
8 myColl.Add "みかん"
9 myColl.Add "ぶどう"
10
11 Debug.Print myColl.Item(1) '//自作のコレクションから1番目のデータを参照
12 Debug.Print myColl(2) '//自作のコレクションから2番目のデータを参照
13 Debug.Print myColl.Count '//自作のコレクションのデータ数を算出
14
15End Sub
16
ワークシートコレクションはExcelで既に定義されたオブジェクトで、コレクションの一種のため、
上述したようにFor
ループを用いて
順番にブックのワークシートの情報を調べることができます。
1
2Sub ワークシートコレクションをループするサンプル()
3 Dim counter As Long
4 Dim currentSheet As Worksheet '//ForEachループで使用するワークシートオブジェクト変数
5
6 '//ループ処理によって各シートの名前を取得したい
7 '////インデックス番号のカウンターを用いたForループの場合
8 For counter = 1 To ThisWorkbook.Worksheets.Count
9 Debug.Print ThisWorkbook.Worksheets(counter).name
10 Next counter
11
12 '////For Each機構を利用したループの場合
13 For Each currentSheet In ThisWorkbook.Worksheets
14 Debug.Print currentSheet.name
15 Next currentSheet
16
17End Sub
18
ワークシートコレクションが属するブック
ワークシートコレクションを使用する際に注意すべきことは、
調べたいワークシートコレクションがどのブックに属しているかということを明確に理解しておくべきということです。
下のコードでも示すように、
Worksheets
でどのブックのワークシートを取り扱うかということは、
Worksheets
のドットの前のブックオブジェクトがどのようなブックを指しているかということで決まります。
1
2Sub ワークシートコレクションが属するブック()
3 Dim targetWSColl As Sheets
4 Dim newBook As Workbook
5
6 Set targetWSColl = ThisWorkbook.Worksheets '// 1. マクロを記述しているブックのワークシートコレクションを変数に入れる
7
8 Set targetWSColl = ActiveWorkbook.Worksheets '// 2. 現在開いているブックのワークシートコレクションを変数に入れる
9
10 Set newBook = Workbooks.Add '// 新しくブックを作成
11 Set targetWSColl = newBook.Worksheets '// 3. 新しく作成したブックのワークシートコレクションを変数に入れる
12 newBook.Close False
13End Sub
14
現在開いているブックのシートを取り扱いたいのに、
ThisWorkbook.Worksheets
を参照したりすることがないように気をつける必要があります。
各シートから取得したい範囲のデータを集約するサンプル
各シートのA1~A3セルの内容をメッセージ本文として集約するサンプルです。
1
2Sub 各シートから取得したい範囲のデータを集約するサンプル()
3 Dim currentSheet As Worksheet
4 Dim aggregatedDataText As String
5 '// 全シートについてセルの内容を調査
6 For Each currentSheet In ThisWorkbook.Worksheets
7 '// A1セルの内容取得
8 aggregatedDataText = aggregatedDataText & currentSheet.Range("A1").Value & ";"
9 '// A2セルの内容取得
10 aggregatedDataText = aggregatedDataText & currentSheet.Range("A2").Value & ";"
11 '// A3セルの内容取得
12 aggregatedDataText = aggregatedDataText & currentSheet.Range("A3").Value
13 aggregatedDataText = aggregatedDataText & vbLf
14 Next currentSheet
15
16 aggregatedDataText = "各シートから集めたデータはこちら" & vbLf & vbLf & aggregatedDataText
17
18 '// 取得した内容をメッセージとして表示
19 MsgBox aggregatedDataText, vbOKOnly, "各シートから取得したい範囲のデータを集約するサンプル"
20End Sub
21
データを出力する
次は、取得対象のデータを集約し、出力する処理について記します。
前提:シートに存在するデータの様式がそろっていること
まず前提として、
シート状の、取得対象のデータの様式が揃っていなければなりません。
具体的には、
取得したいセル範囲、セル結合しているか否か、各列の内容とその順番が統一されている必要があります。
セル範囲がワークシートごとにまちまちだったり、項目の順番があるシートだけ逆順になっていたりするとコードが複雑化してしまいます。
今回は、
2列のセル範囲を持つ売上金データをまとめてみます。
シートは、都道府県ごとのシートが複数と、
例外となるシートも存在することとします。
対象となるシートの名前は「〇〇データ」であるすべてのシートとします。
なお、デモに用いたファイルは
こちらからダウンロードできます。
シートに出力する場合
まず、データを集めるシートと同じブックに、
まとめ用シートを作成することを考えます。
データの集約と出力は、
コピーペーストではなく、値の取得・出力で行います
理由は、コピーペーストの場合、コメントや条件付き書式など、
不必要なデータも伴ってしまう可能性があるからです。
1
2Sub シートに出力する場合01_月単位でまとめないバージョン()
3 Dim currentSheet As Worksheet '// ループ用
4 Dim matomeSheet As Worksheet '// 作成したまとめシート
5 Dim sheetDataDic As Object '//シートのデータ格納用のDictionary
6 Dim currentSheetName As Variant '// ループ用
7 Dim rowNumCursor As Long '// 操作する行番号を示すカーソル
8
9 '// シートのデータ格納用のDictionaryを設定
10 Set sheetDataDic = CreateObject("Scripting.Dictionary")
11
12 '// 全シートごとにセルの内容を調査
13 For Each currentSheet In ThisWorkbook.Worksheets
14 '// 「〇〇データ」の名前のシートのみ調査
15 If InStr(currentSheet.Name, "データ") > 0 Then
16 sheetDataDic.Add currentSheet.Name, currentSheet.Range("B8:C19").Value
17 End If
18 '// まとめシートがすでにある場合は削除
19 If currentSheet.Name = "まとめ" Then
20 Application.DisplayAlerts = False
21 currentSheet.Delete
22 Application.DisplayAlerts = True
23 End If
24 Next currentSheet
25
26 '// まとめシートを作成
27 Set matomeSheet = ThisWorkbook.Worksheets.Add
28 matomeSheet.Name = "まとめ"
29
30 '// まとめシートに各シートの売上金などのデータを貼り付け
31 rowNumCursor = 3
32 '//// ヘッダー文字列を設定
33 matomeSheet.Cells(2, 2).Value = "都道府県"
34 matomeSheet.Cells(2, 3).Value = "計上月"
35 matomeSheet.Cells(2, 4).Value = "売上金"
36 '//// 各シートごとにデータを処理
37 For Each currentSheetName In sheetDataDic.Keys
38 '// 都道府県名を書き込み
39 matomeSheet.Range( _
40 matomeSheet.Cells(rowNumCursor, 2), _
41 matomeSheet.Cells(rowNumCursor + 11, 2) _
42 ).Value = currentSheetName
43 '// 売上金などデータを書き込み
44 matomeSheet.Range( _
45 matomeSheet.Cells(rowNumCursor, 3), _
46 matomeSheet.Cells(rowNumCursor + 11, 4) _
47 ).Value = sheetDataDic(currentSheetName)
48
49 '// 次の都道府県へ
50 rowNumCursor = rowNumCursor + 12
51 Next currentSheetName
52
53End Sub
54
実行すると、
「まとめ」シートが作成されます。
すべての都道府県シートにおいてB8:C19
の範囲でデータが有るため、
コードが簡潔に書けています。
シートに出力する場合(並べ替え処理を追加)
次に、上の関数に、
計上月順に並べ替えを行う処理を追加します。
1
2Sub シートに出力する場合02_月単位でまとめるバージョン()
3 Dim currentSheet As Worksheet '// ループ用
4 Dim matomeSheet As Worksheet '// 作成したまとめシート
5 Dim sheetDataDic As Object '//シートのデータ格納用のDictionary
6 Dim currentSheetName As Variant '// ループ用
7 Dim rowNumCursor As Long '// 操作する行番号を示すカーソル
8
9 '// シートのデータ格納用のDictionaryを設定
10 Set sheetDataDic = CreateObject("Scripting.Dictionary")
11
12 '// 全シートごとにセルの内容を調査
13 For Each currentSheet In ThisWorkbook.Worksheets
14 '// 「〇〇データ」の名前のシートのみ調査
15 If InStr(currentSheet.Name, "データ") > 0 Then
16 sheetDataDic.Add currentSheet.Name, currentSheet.Range("B8:C19").Value
17 End If
18 '// まとめシートがすでにある場合は削除
19 If currentSheet.Name = "まとめ" Then
20 Application.DisplayAlerts = False
21 currentSheet.Delete
22 Application.DisplayAlerts = True
23 End If
24 Next currentSheet
25
26 '// まとめシートを作成
27 Set matomeSheet = ThisWorkbook.Worksheets.Add
28 matomeSheet.Name = "まとめ"
29
30 '// まとめシートに各シートの売上金などのデータを貼り付け
31 rowNumCursor = 3
32 '//// ヘッダー文字列を設定
33 matomeSheet.Cells(2, 2).Value = "都道府県"
34 matomeSheet.Cells(2, 3).Value = "計上月"
35 matomeSheet.Cells(2, 4).Value = "売上金"
36 '//// 各シートごとにデータを処理
37 For Each currentSheetName In sheetDataDic.Keys
38 '// 都道府県名を書き込み
39 matomeSheet.Range( _
40 matomeSheet.Cells(rowNumCursor, 2), _
41 matomeSheet.Cells(rowNumCursor + 11, 2) _
42 ).Value = currentSheetName
43 '// 売上金などデータを書き込み
44 matomeSheet.Range( _
45 matomeSheet.Cells(rowNumCursor, 3), _
46 matomeSheet.Cells(rowNumCursor + 11, 4) _
47 ).Value = sheetDataDic(currentSheetName)
48
49 '// 次の都道府県へ
50 rowNumCursor = rowNumCursor + 12
51 Next currentSheetName
52
53 '// 計上月と都道府県の列を入れ替える
54 matomeSheet.Columns(3).Cut
55 matomeSheet.Columns(2).Insert Shift:=xlToRight
56
57 '// 計上月順に並び替えを行う
58 With matomeSheet.Sort
59 .SortFields.Clear
60 .SortFields.Add2 Key:=matomeSheet.Range( _
61 matomeSheet.Cells(3, 2), _
62 matomeSheet.Cells(rowNumCursor - 1, 2) _
63 ) _
64 , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
65 .SetRange matomeSheet.Range( _
66 matomeSheet.Cells(3, 2), _
67 matomeSheet.Cells(rowNumCursor - 1, 4) _
68 )
69 .Header = xlYes
70 .MatchCase = False
71 .Orientation = xlTopToBottom
72 .SortMethod = xlPinYin
73 .Apply
74 End With
75
76End Sub
77
実行すると、
「まとめ」シートが作成され、
なおかつデータが月の若い順からソートされて表示されます。
このように、
集約して別シートとしてデータをまとめれば、
並び替えや分析も容易になります。
シートに出力する場合(書式も合わせる)
今度は、
データのテーブルの書式も都道府県シートの元データテーブルに
寄せてみます。
1
2Sub シートに出力する場合03_書式も合わせるバージョン()
3 Dim currentSheet As Worksheet '// ループ用
4 Dim matomeSheet As Worksheet '// 作成したまとめシート
5 Dim sheetDataDic As Object '//シートのデータ格納用のDictionary
6 Dim currentSheetName As Variant '// ループ用
7 Dim rowNumCursor As Long '// 操作する行番号を示すカーソル
8
9 '// シートのデータ格納用のDictionaryを設定
10 Set sheetDataDic = CreateObject("Scripting.Dictionary")
11
12 '// 全シートごとにセルの内容を調査
13 For Each currentSheet In ThisWorkbook.Worksheets
14 '// 「〇〇データ」の名前のシートのみ調査
15 If InStr(currentSheet.Name, "データ") > 0 Then
16 sheetDataDic.Add currentSheet.Name, currentSheet.Range("B8:C19").Value
17 End If
18 '// まとめシートがすでにある場合は削除
19 If currentSheet.Name = "まとめ" Then
20 Application.DisplayAlerts = False
21 currentSheet.Delete
22 Application.DisplayAlerts = True
23 End If
24 Next currentSheet
25
26 '// まとめシートを作成
27 Set matomeSheet = ThisWorkbook.Worksheets.Add
28 matomeSheet.Name = "まとめ"
29
30 '// まとめシートに各シートの売上金などのデータを貼り付け
31 rowNumCursor = 3
32 '//// ヘッダー文字列を設定
33 matomeSheet.Cells(2, 2).Value = "都道府県"
34 matomeSheet.Cells(2, 3).Value = "計上月"
35 matomeSheet.Cells(2, 4).Value = "売上金"
36 '//// 各シートごとにデータを処理
37 For Each currentSheetName In sheetDataDic.Keys
38 '// 都道府県名を書き込み
39 matomeSheet.Range( _
40 matomeSheet.Cells(rowNumCursor, 2), _
41 matomeSheet.Cells(rowNumCursor + 11, 2) _
42 ).Value = currentSheetName
43 '// 売上金などデータを書き込み
44 matomeSheet.Range( _
45 matomeSheet.Cells(rowNumCursor, 3), _
46 matomeSheet.Cells(rowNumCursor + 11, 4) _
47 ).Value = sheetDataDic(currentSheetName)
48
49 '// 次の都道府県へ
50 rowNumCursor = rowNumCursor + 12
51 Next currentSheetName
52
53 '// 計上月と都道府県の列を入れ替える
54 matomeSheet.Columns(3).Cut
55 matomeSheet.Columns(2).Insert Shift:=xlToRight
56
57 '// 計上月順に並び替えを行う
58 With matomeSheet.Sort
59 .SortFields.Clear
60 .SortFields.Add2 Key:=matomeSheet.Range( _
61 matomeSheet.Cells(3, 2), _
62 matomeSheet.Cells(rowNumCursor - 1, 2) _
63 ) _
64 , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
65 .SetRange matomeSheet.Range( _
66 matomeSheet.Cells(3, 2), _
67 matomeSheet.Cells(rowNumCursor - 1, 4) _
68 )
69 .Header = xlYes
70 .MatchCase = False
71 .Orientation = xlTopToBottom
72 .SortMethod = xlPinYin
73 .Apply
74 End With
75
76 '// 書式を元のデータテーブルに合わせる
77 '//// 罫線
78 matomeSheet.Range( _
79 matomeSheet.Cells(2, 2), _
80 matomeSheet.Cells(rowNumCursor - 1, 4) _
81 ).Borders.LineStyle = xlContinuous
82 '//// ヘッダー背景色、中央揃え
83 With matomeSheet.Range( _
84 matomeSheet.Cells(2, 2), _
85 matomeSheet.Cells(2, 4) _
86 )
87 .Interior.Color = 15917529
88 .HorizontalAlignment = xlCenter
89 End With
90End Sub
91
実行すると、
「まとめ」シートが作成され、ソートされ、
なおかつヘッダーの背景色や罫線が元テーブル仕様に変更されます。
テキストファイルに出力する場合
シートに出力せずにテキストファイルに出力する場合でも、
いったん作業用シートとして一つのシートにまとめ、
並び替えなどを行った後にテキストに出力すると便利な場合があるでしょう。
コードは以下のようになります。
1
2Sub テキストファイル_CSV_に出力する場合()
3 Dim currentSheet As Worksheet '// ループ用
4 Dim matomeSheet As Worksheet '// 作成したまとめシート
5 Dim sheetDataDic As Object '//シートのデータ格納用のDictionary
6 Dim currentSheetName As Variant '// ループ用
7 Dim rowNumCursor As Long '// 操作する行番号を示すカーソル
8
9 '// シートのデータ格納用のDictionaryを設定
10 Set sheetDataDic = CreateObject("Scripting.Dictionary")
11
12 '// 全シートごとにセルの内容を調査
13 For Each currentSheet In ThisWorkbook.Worksheets
14 '// 「〇〇データ」の名前のシートのみ調査
15 If InStr(currentSheet.Name, "データ") > 0 Then
16 sheetDataDic.Add currentSheet.Name, currentSheet.Range("B8:C19").Value
17 End If
18 '// まとめシートがすでにある場合は削除
19 If currentSheet.Name = "まとめ" Then
20 Application.DisplayAlerts = False
21 currentSheet.Delete
22 Application.DisplayAlerts = True
23 End If
24 Next currentSheet
25
26 '// まとめシートを作成
27 Set matomeSheet = ThisWorkbook.Worksheets.Add
28 matomeSheet.Name = "まとめ"
29
30 '// まとめシートに各シートの売上金などのデータを貼り付け
31 rowNumCursor = 2
32 '//// ヘッダー文字列を設定
33 matomeSheet.Cells(1, 1).Value = "都道府県"
34 matomeSheet.Cells(1, 2).Value = "計上月"
35 matomeSheet.Cells(1, 3).Value = "売上金"
36 '//// 各シートごとにデータを処理
37 For Each currentSheetName In sheetDataDic.Keys
38 '// 都道府県名を書き込み
39 matomeSheet.Range( _
40 matomeSheet.Cells(rowNumCursor, 1), _
41 matomeSheet.Cells(rowNumCursor + 11, 1) _
42 ).Value = currentSheetName
43 '// 売上金などデータを書き込み
44 matomeSheet.Range( _
45 matomeSheet.Cells(rowNumCursor, 2), _
46 matomeSheet.Cells(rowNumCursor + 11, 3) _
47 ).Value = sheetDataDic(currentSheetName)
48
49 '// 次の都道府県へ
50 rowNumCursor = rowNumCursor + 12
51 Next currentSheetName
52
53 '// 計上月と都道府県の列を入れ替える
54 matomeSheet.Columns(2).Cut
55 matomeSheet.Columns(1).Insert Shift:=xlToRight
56
57 '// 計上月順に並び替えを行う
58 With matomeSheet.Sort
59 .SortFields.Clear
60 .SortFields.Add2 Key:=matomeSheet.Range( _
61 matomeSheet.Cells(3, 1), _
62 matomeSheet.Cells(rowNumCursor - 1, 1) _
63 ) _
64 , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
65 .SetRange matomeSheet.Range( _
66 matomeSheet.Cells(3, 1), _
67 matomeSheet.Cells(rowNumCursor - 1, 3) _
68 )
69 .Header = xlYes
70 .MatchCase = False
71 .Orientation = xlTopToBottom
72 .SortMethod = xlPinYin
73 .Apply
74 End With
75
76 '// まとめシートを別ブックとして取り出す
77 matomeSheet.Move
78
79 '// まとめシートをCSV形式のテキストファイルとしてブックと同じフォルダに保存
80 ActiveWorkbook.SaveAs Filename:=ThisWorkbook.Path & "\" & "まとめ.csv" _
81 , FileFormat:=xlCSVUTF8, CreateBackup:=False
82 ActiveWorkbook.Close
83
84End Sub
85
matomeSheet.Move は、
CSVとして保存する前にかならず行うべき処理です。
これをせずにCSV形式でブックを保存すると、
マクロが記載されたブックをCSVに変換することになり、不本意な結果に繋がる可能性があります。
終わりに
ワークシートのデータを集約し、
まとめる方法についていくつかのバリエーションを見てきました。
より複雑なケースにも対応できるように、
ワークシートに記載するデータの様式を整えておくと便利でしょう。
関連記事
- [教えて!VBA] 第11回 マクロからファイルを新しく名付けて保存する際の注意点とは??
- [教えて!VBA] 第10回 Excelのシートの移動・削除を禁止する方法 & マクロ実行時だけ許可するにはどうすればいいの??
- [教えて!VBA] 第9回 フォントやボタンをカスタマイズできるメッセージボックスを作成するにはどうすればいいの??
- [教えて!VBA] 第8回 変更前のセルの値を保持/利用するにはどうすればいいの??
- [教えて!VBA] 第7回 フォルダを開く(+ファイルを操作する)にはどうすればいいの??