Excelの画面から操作することはほとんどマクロで実行可能です。
画面から操作した方が簡単な場合も有りますが、データ量が多い場合、扱うシートが多い場合、定期的に同じような作業をする場合、作業時間の短縮化、正確性の向上に寄与できると思います。
このサイトではデータの集計や報告書の作成を仕事にしている方が実際に使えるモジュール(サブプロシージャ)を紹介していきます。
(1) マクロの登録準備
マクロを登録したり呼び出したりするのには、マクロのリボンが表示状態にしておく必要があります。
バージョンによって異なるかもしれませんが『ホーム』リボンを右クリックして、『リボンのユーザー設定』で開発ツールにチェックを入れて下さい
3.マクロの登録
マクロの登録
マクロの登録方法は2種類あります
(1)マクロの記録
マクロの記録ボタンを押してから、マクロ名を入力し、シートに対する操作(列の削除とか、ソートなど)をします。終わったならば『記録終了』を押します。定型的な作業には使えますが、条件にあったデータのみを処理する場合などには使いにくい思います。
(2)モジュールの登録
これで登録の準備はできました。
次にマクロを使う上で最低限必要な用語を説明します。
4.最小限必要な用語と考え方
(1)オブジェクト
(2)Dim ステーメント(変数の宣言)
Dim i as long (数字-小数なし) |
Dim range1 as string (文字) |
Dim su1 as double(数字-小数あり) |
(3)代入式(右辺左辺同一変数の考え方)
概念的な説明だけでは実感して頂けないと思いますので、実際のマクロを作成して実施前と実施後を比較していきます。
マクロはシート単位でおこなうものと、行やセル単位で行うものがあります。
後者の方が応用範囲が広く複雑な処理もできるのですが、少し難しいです。ここではシート単位でのマクロの使用例を見ていきましょう。これから下の表をマクロを使って加工していきます。元の表は何れも下記の表となります。
(全て実在していないデータです)
5.マクロの使用例(シート)
(1)クリアー
Sub test01() |
Range("A2:G15").Clear |
End Sub |
あるいは |
Sub test02() |
Range("A1").CurrentRegion.Offset(1, 0).Clear |
End Sub |
クリアーの重要性について
(2)ソート
Sub test03() |
Range("A1").Sort Key1:=Range("F1"), Order1:=xlAscending, Header:=xlYes |
End Sub |
Range("A1") ⇒ Range("A1:G15") とします
(3)列の削除
Sub test04() ' |
Columns("D:D").Delete |
End Sub |
(4)列の切り取りと挿入
Sub test05() Columns("D:D").Cut |
Columns("G:G").Insert Shift:=xlToRight |
End Sub |
(5)シート内容を全てコピーする
Sub test06() |
Sheets("Sheet3").Select |
Cells.Copy |
Sheets("Sheet4").Select |
Cells.Select |
ActiveSheet.Paste |
Application.CutCopyMode = False 'クリップボードのクリア |
End Sub |
6.マクロの使用例(行及びセル単位の処理)
ここまでの処理は画面から操作しても可能な処理でした。
また、マクロの記録でも簡単にモジュールを作成することができます。
ここから先が本格的なマクロ処理となります。
この処理をするためには『For・・・ Next』ステートメントまたは『Do・・・ Loop』ステートメントを使います。
(1)行ごとに判断結果をシートに書き込む
IF文は関数でも簡単にできるのですが、マクロを使うと複雑な分岐をするIF文を比較的簡単に作ることができます。
Sub test07() |
Dim MaxRow As Long |
Dim i As Long |
Sheets("Sheet4").Select '処理対象を Sheet4 とする |
MaxRow = Cells(Rows.Count, 1).End(xlUp).Row '1列目(A列)を参照し 'て最大 行を求める |
For i = 2 To MaxRow '2からMaxRow(最大行)まで繰り返す |
If Cells(i, "F") < 20 Then 'F列i行目の値が20未満かどうか判断し |
Cells(i, "H") = 1 '20未満ならH列 I 行目に 1 をセットする |
Else |
Cells(i, "H") = 0 'そうでなければH列 I 行目に 0 をセットする |
End If |
Next 'Iに 1を加算して "For" の位置まで戻る |
End Sub |
解説
For で始まりNext で終わる部分
If で始まり End If で終わる部分
If 条件式 Then |
条件が成立した場合の処理 |
Else |
条件が成立しなかった場合の処理 |
End If |
他に罫線やタイトルなど追加したい部分があります。またどちらの文もバリエーションが多くあります。しかしこの『For・・・Next』や『If ・・・End if』が自在に使えるようになることはプログラミング上非常に重要なことです。他の言語でも類似表現はよく使われます。
(2)行を削除する及び高速化
For i = MaxRow To 2 Step -1 |
If Cells(i, "C") = "男" Then |
Cells(i, "C").EntireRow.Delete |
End If |
Next |
削除文を実行すると処理速度が遅くなります。削除した瞬間に表示される表を1行減らしたものに再表示するからです。この画面表示をやめさせる制御と、シート上の関数の計算を一時的に停止する制御文をセットで使う習慣を身につけると何かと便利です。
Application.ScreenUpdating = False | ’画面表示の抑止 |
Application.Calculation = xlCalculationManual | ’計算の抑止 |
これはプログラムが終了する前に元に戻しておく必要があります
Application.ScreenUpdating = True | ’画面表示の再開 |
Application.Calculation = xlCalculationAutomatic | ’計算の実施 |
通しで実行すると下記のようになります
Public Sub test07() |
Dim MaxRow As Long |
Dim i As Long |
Application.ScreenUpdating = False |
Application.Calculation = xlCalculationManual |
MaxRow = Cells(Rows.Count, 1).End(xlUp).Row |
For i = MaxRow To 2 Step -1 |
If Cells(i, "C") = "男" Then |
Cells(i, "C").EntireRow.Delete |
End If |
Next |
Application.ScreenUpdating = True |
Application.Calculation = xlCalculationAutomatic |
|
ではこの高速処理をした場合としなかった場合の時間差はどのくらいかというと1000件のデータを処理した場合
高速化しなかった場合 13.84秒
高速化した場合 0.234秒
と圧倒低な差がつきました。ちなみにF列には =DATEDIF(E17,TODAY(),"Y")という生年月日から年齢を算出する関数が入っています。
(3)別なシートへのデータ追加(同一Book)
日次データを月次データに追加する、複数名で入力したデータを一つにまとめる時の方法です。
少し長くなるので、先に手順をリストアップします(あくまで一例です)
①追加される側のシートを選択する。
②データの行数をカウントする
③行数 + 1の値を変数に記録する。
④追加するシートを選択する。
⑤データの行数をカウントする。
⑥コピー範囲を定める
⑦コピー先のシートに貼り付ける
上記手順に従ってコードを作成します。事例ではSheet7のデータ部分(2行目以降)に追加します。追加する側、される側も10列とします。
Public Sub test08() |
Dim MaxRow As Long |
Dim i As Long |
Dim Max1 As Long |
Dim RNG As String |
Sheets("Sheet4").Select '追加される側 |
MaxRow = Cells(Rows.Count, 1).End(xlUp).Row '最大行の把握 |
Max1 = MaxRow + 1 '追加先の行位置を決める |
Sheets("Sheet7").Select '追加する側 |
MaxRow = Cells(Rows.Count, 1).End(xlUp).Row '最大行の把握 |
RNG = "A2:J" & MaxRow '追加する範囲を決める |
Range(RNG).Copy |
Sheets("Sheet4").Range("A" & Max1).PasteSpecial Paste:=xlAll |
Application.CutCopyMode = False |
End Sub |
Range(RNG).Copy |
Sheets("Sheet4").Range("A" & Max1).PasteSpecial Paste:=xlAll |
Sheets("Sheet7").Range(RNG).Copy |
Sheets("Sheet4").Range("A" & Max1).PasteSpecial Paste:=xlAll |
7.マクロの使用例(シート間の処理)
(1)別なシートへのデータ追加(異なる一Book)
前述のSheet7をSheet4に追加する処理ですが、Sheet4が異なるBookにある場合の処理方法です。
Bookが異なるとすべきことが急に増えます。
作業は下記の通りです。
①(追加されるシート)selectする
②現在のBook名を記録する
③データの行数をカウントする
④行数 + 1の値を変数に記録する。
⑤別のシート(『動作環境』から)追加する(インポートする)Bookの所在と名称を参照する
⑥上記Bookが存在するか調べる(存在しなかった場合はエラーメッセージを出して終了する。
⑦Bookを開けなかった場合の処理を作成する
⑧追加すべきデータの存在するBookを開き、Book名を記録する(xlBookbB)
⑨(xlBookbB)のSheet7をselectする。
⑩追加するデータの行数を求め、コピーする範囲を決める。
⑪コピー先のシートに貼り付ける
⑪(xlBookbB)をクローズする
この時確認メッセージを抑止するには
クローズ文の前後に Application.DisplayAlerts = False、 ' Application.DisplayAlerts = Trueを記述する。
今回は①のBook名を現在のBookの"動作環境"というシートの"C2"のセルに記述するという形を取ります。モジュールの中にBook名を記入することも可能ですが、対象のBookが変わるたびにモジュールを書き換えるのは不便だなので、シート上に記述することにします。
前回と同じように手順を書いてみましょう。
上記手順に従ってコードを作成します。事例ではSheet7のデータ部分(2行目以降)に追加します。追加する側、される側も10列とします。
Sub test09() |
Dim i As Long |
Dim MaxRow As Long |
Dim FNAME1 As String |
Dim xlBookA As Workbook |
Dim xlBookB As Workbook |
Dim Max1 As Long |
Dim RNG As String |
'Sheet4シートの範囲確認 |
Sheets("Sheet4").Select |
Set xlBookA = ThisWorkbook |
' 最終行の取得 |
MaxRow = Cells(Rows.Count, 1).End(xlUp).Row |
' 貼付先の行数を算出する |
Max1 = MaxRow + 1 |
' インポートファイルの情報取得 |
Sheets("動作環境").Select '開くファイル名が書かれているシートを開く |
FNAME1 = Range("C2") '開くファイル名を取得する 'C2には具体的に『D:¥2020¥FILE_B.xlsx』などの名前が入ります |
If Dir(FNAME1) <> "" Then 'ファイルが存在するか確認する |
Set xlBookB = Workbooks.Open(FNAME1) 'ファイルを開いてxlBookBとする |
Else |
MsgBox FNAME1 & " ファイルが存在しません。プログラムを終了します" |
Exit Sub |
End If |
' インポート処理 |
Sheets("Sheet7").Select |
MaxRow = Cells(Rows.Count, 2).End(xlUp).Row |
RNG = "A2:J" & MaxRow |
Range(RNG).Copy |
xlBookA.Worksheets("Sheet4").Range("A" & Max1).PasteSpecial Paste:=xlAll |
Application.DisplayAlerts = False |
xlBookB.Close |
xlBookA.Worksheets("Sheet4").Activate |
Application.DisplayAlerts = True |
End Sub |
Range(RNG).Copy |
Sheets("Sheet4").Range("A" & Max1).PasteSpecial Paste:=xlAll |
Sheets("Sheet7").Range(RNG).Copy |
Sheets("Sheet4").Range("A" & Max1).PasteSpecial Paste:=xlAll |
8.関数の設定(表計算式を組み込む)
関数をシートに組み込むと処理が遅くなるのであまり好ましいことではありません。しかしながら関数を使った方が便利な場合があります。それは下記のようなケースです。
1.VBAの関数に相当するメソッドが使いにくい
マクロのメソッドは項目(変数)の定義に反する処理にが厳しいのでエラー処理の記述が大変
またそのエラー処理が正しくされているか確認のデバッグに時間が掛かる
文が長くなる
2.Sheetの値を確認する時どのような処理をしているか分かりにくい(特にIF文やVlookup文)
関数は手作業で追加しても良いのですがデータの行数に合わせてCopyしたり、関数の入力範囲を消さないような配慮も必要になるので『マクロを使ってExcel関数を』データの数に合わせて書き込んだ方が便利かと思います。
文例は以下の通りになります
Sub test10() |
Dim i As Long |
Dim MaxRow As Long |
Sheets("Sheet4").Select |
' 最終行の取得 |
MaxRow = Cells(Rows.Count, 1).End(xlUp).Row |
'関数を入れる行をSelectして相対表記で関数をセットする |
Range("F2").Select '関数を設定する最初の行 |
ActiveCell.FormulaR1C1 = "=DATEDIF(RC[-1],TODAY(),""Y"")" '生年月日と当日で年齢を算出 |
Selection.AutoFill Destination:=Range("F2:F" & i), Type:=xlFillDefault 'F2のセルを最終行までコピーする |
Application.CutCopyMode = False 'クリップボードのクリアー |
End Sub |
解説 関数は必ず相対表記で記述する必要があります。
VBAでCopyすると関数を行に合わせて書き換えることをしてくれないからです。相対表記がなれない方は一度シート上で関数を打ち込み、その後『マクロの記録』でその関数の"="を削除し、更に"="を挿入すれば、相対表記された関数を入手することができます。