オフィス・ブールは、Excel全般について皆さまのお仕事をサポートします。
オフィス・ブールのロゴ
 代 表   : 末永 尚登(スエナガ ヒサト)
 E-mail  : suenaga@officeboole.com
 携 帯  : 070-4087-0025
 TEL/FAX    : 095-893-6090



VBAでプログラミングを体験してみよう


目 次
  1. どういう結果がほしいか考える(グループ小計を得る)
  2. 計算手順(アルゴリズム)を考える
  3. VBAの命令に置き換える(コーディング)
  4. 誤りを見つけて修正(デバッグ)する
  5. 動作結果を確認する
前項で紹介したように、記録マクロは誰にでも簡単に作ることができますが、それだけではどうしても実現できない処理というものが実際には存在します。
ここでは下図のような分類コードごとに金額の小計をとる場合を考えてみます。

1.どういう結果がほしいか考える(グループ小計を得る)

まずプログラミングするにあたっては、最終的にどういう結果が欲しいのかを明確にしなければいけません。 当たり前のことですね。

ワークシート上に図1のような書籍コードと金額を記した表があるとします。
書籍コードはそれぞれの書籍ごとに付けられており、その書籍コードの1桁目は分野ごとの分類コードになっています。

コード構成
コード構成

今回はこの表を元に、分類コードごとに金額を集計し、その結果を右表の各小計欄に出力する処理をプログラミングしてみることにします。

図1
図1 集計元の表

2.計算手順(アルゴリズム)を考える

どういう結果がほしいかをはっきりさせたなら、次はそれをどういう手順で実現すればよいかを考えます。

通常、この表で小計を求めようとした場合、書籍コード順(アルファベット順)に並び替えをして、次に書籍コードの1桁目が同じ記号のものどおしを集計し、右表の小計欄に書き出すという手順が簡単そうですね。(データが並び替わってもいい場合)
この場合の並び替えは、Excelの「並び替え」機能を使って、キーを書籍コードに指定し、昇順に並び替えを行えば、記録マクロでも問題なく実行できそうです。
しかし分野ごと(分類コード別)の集計はどうでしょう?
Excelにも皆さんご存知のように「集計」機能があります。これは同じ内容の項目をグループ化して集計を行ってくれますが、今回の場合のように項目の一部を比較して集計することはできません。
手作業でやるなら、書籍コードの隣に列を1つ挿入し、その列に書籍コードの1桁だけをLeft関数などで取り出せば、「集計」機能でも実現できます。でもここではVBAを使ってのプログラミングを体験していただきたいので、すべてVBAコードで処理する方法を考えてみます。

ところで答えを得るまでの計算手順は、アルゴリズムとも言います。
簡単な処理なら、特別アルゴリズムなどを持ち出さなくとも、手順はすぐに思いつきますが、少し複雑な処理になるとそうもいきません。 無駄な試行錯誤を減らすためにも、簡単なものでいいですからいったん紙に書き出してみましょう。
アルゴリズムは文章で表現してもよいのですが、ここではもっと分かりやすいように図で表すことにします。 図もどんなものでもよいと思いますが、今回は一般的なフローチャート図を用いてみます。

図2 全体のフローチャート
図2 全体のフローチャート

図2が全体のフローチャートです。
縦棒2重線のボックスは、それぞれが1つのまとまった処理を表しています。

この集計処理は大きく分けると1)並び替え(ソート)と2)集計に分けることができそうです。

並び替えはマクロ記録で生成されたコードを利用するとして、問題なのは集計処理の部分です。
すでに書籍コード順に並び替えが終わっているので、分類コードもA類、B類、C類のグループごとにまとまっているはずですが、ではどのようにしてグループの切れ目を判断すればよいでしょうか?

グループの切れ目を判断しながら処理を行うためには、現在処理中の分類コードと1つ前に処理した分類コードとを比較し、同じコードかどうかを判定するという作業が必要になります。
この部分の処理はも少し詳細に表さないとコードに落とせないので、別に図解することにします。

図3 集計処理のフローチャート
図3 集計処理のフローチャート


ということで、集計処理をもう少し詳しく表したのが図3になります。
ループ処理と書かれたオレンジ色のボックスは上下で中身を挟んだ形になっています。
この部分は繰り返し処理を表しており、最後のデータを読み終わるまで内部の処理を繰り返しなさいという意味です。

まず1件目のデータを読んだら、書籍コードの1桁目(分類コード)を取得します。そして保存されている分類コードと比較します。
実際には1件目のデータについては、以前のデータは存在しませんので、この比較自体をスキップさせるか、または小計の出力が無効となるような処理を付け加える必要があります。

次に現在読み込んでいるデータの金額部分を小計の箱に加算します。
加算したら現在の分類コードを、次のデータとの比較のために保存用の箱に格納しておきます。
そして次のデータを読み込みます。読み込んだらループ処理の先頭に戻って、現在の書籍コードから分類コードを取り出します。
現在の分類コードと保存してあった分類コードを比較し、同じであれば小計の箱に金額を加算し、違っていれば今まで加算した小計の箱の中身を小計欄に出力します。

小計を出力したら、いったん中身を消去します。
そして現在読み込んでいるデータについては、まだ何も処理していませんからその金額を小計箱に加算してあげます。

ここで注意しなければいけないのは最後のデータに対する扱いです。
最後のデータに関しては、ループ内の処理だけですと、分類コードが以前と異なっていれば今までの小計を出力しますが、現在の金額は小計として出力する機会がありません。
また分類コードが1つ前のデータと同じ場合も、そのまま小計箱に加算されるだけで出力の機会がないまま終わってしまうことになります。
したがってループを抜けた後、必ず最後のデータに対しての出力処理を加えておかなければなりません。

3.VBAの命令に置き換える(コーディング)

完璧なアルゴリズムを考えるのは、処理が複雑なほど難しくなりますので、実際にはある程度大まかな処理の流れを描いたら、具体的な命令(VBAコード)に置き換えてみます。
ではまず最初に並び替えの部分をVBAの命令に置き換えてみましょう。

この部分はマクロ記録を利用できますので、「ツール」→「マクロ」→「新しいマクロの記録」を実行して、 A1からB10までを選択した状態で「データ」→「並び替え」を実行します。並び替えのキーに「書籍コード」を選択して並び順を昇順にします。
そにまま実行すると書籍コードの若い順にデータが並び変わります。並び替えが完了したらマクロ記録を解除して、コードを確認(Alt + F11)してみてください。

図4
図4 並び替えのマクロ

このままでも取りあえずはOKですが、後で見たとき分かりづらいので必要な部分だけを残して、後は削除することにしましょう。

選択した範囲に対してSortメソッドが実行されていますが、後ろに引数がたくさん並べて記述されています。この引数の中から必要なものだけを残すことにします。
まずKey1は並べ替えのキーとなる項目を指定してるので必要です。
Order1は項目の並び順を指定しています。省略した場合もxlAscending(昇順)になりますが、後から見てもわかりやすいように残しておくことにしましょう。
HeaderでxlGuessを指定すると、範囲の先頭行がタイトル行であるかどうかを自動的に判断します。 ここもxlGuessのままにしておきます。先頭行がタイトル(見出し)の場合は、自動的にデータ行のみソートします。
後はOrderCustom、MatchCase、Orientation、SortMethodですが、これらは省略しても今回の場合あまり問題ないのですべて削除することにします。
それぞれの意味を知りたい方はヘルプを参照してください。

これでかなり見やすくなりましたが、コードを実行する上で、少し無駄な動きがあります
それは、いったんRange("A1:B10").Selectでデータ範囲を選択しておいて、それからSortメソッドを実行している部分です。
ここは直接Range("A2:B10").Sortと1行で記述しても、問題ありません。
むしろ1行で記述しなければ、Selectメソッドを実行後にSortメソッドを実行する、という無駄な動作が入ってしまいます。 実際の操作では仕方ない動作ですが、コードで実行させる場合はもっと効率よく動作させるために修正が必要なところです。

ではこのコードを別に名前を付けてサブプロシージャとして書き直しましょう。
「挿入」→「プロシージャ」を選択し、プロシージャ名(Shuukei)を入力してください。
サブプロシージャの枠が作られたら、先ほどのソートのコードを転写します。
’(アポストロフィ)を付けると、コメント行(緑色の文字部分)となり、可読性が増します。

図5(修正後)
図5 修正後の並び替えマクロ

次はいよいよ集計処理の部分です。
フローチャートを見ると、まず1件目のデータを読み込んでいます。
これは具体的には、ワークシートの2行目1列目セル(A2)の書籍コードを取得し、その値の先頭から1桁目の文字列を取り出す操作になります。
何だか大変そうですけどやってみましょう。
まず2行目1列目のセルの値を取得するにはCellsオブジェクトを使ってCells(2,1).Valueと表現します。書式はCells(行,列).Valueです。
取得した値はしばらく記憶しておく必要があるので、変数KEYを文字列型で宣言してその中に入れます。変数とはコンピュータに値を一時的に記憶させておく箱のようなものです。

Dim KEY As String
KEY = Cells(2, 1).Value

さらに値の先頭(左側)から1桁目を取り出すには、Left関数を使って上記の式を次のように書き換えます。

Dim KEY As String
KEY = Left(Cells(2, 1).Value, 1)

では連続してセルの値を取得するにはどうしたらよいでしょう?
実はVBAには繰り返しを行わせるのにDo〜Loopという便利な命令があります。
繰り返しの終わりを判断するためにはいくつかの書き方がありますが、ここでは「Do While 繰り返し条件式」の書式で表現してみます。


Dim KEY As String
Dim sKEY As String
Dim i As Long
i = 2
KEY = Left(Cells(i, 1).Value, 1) 
Do While KEY <> ""
    If KEY <> sKEY Then
        '小計を出力
        '小計をクリア
    End If
    '小計に加算
    sKEY = KEY
    i = i + 1
    KEY = Left(Cells(i, 1).Value, 1) 
Loop
	
	

では、このループ処理の説明をしていきましょう。
冒頭で変数iとsKEYを宣言していますが、これは次に計算の対象となるセルの行番号を指定するためと、現在のKEYの保存用(次のKEYと比較するため)に使います。
個々の具体的な処理については、次の段階で説明をする予定のため、今はコメントだけにしています。

まずループに入る前にiに2を代入し、読み取るべき最初のセル位置を決定します。
そして、1件目つまり2行目A列のセルの値を取り出し、その1桁目を変数KEYに格納します。
その後ループに入り、ループ内の処理に入る前に繰り返し条件をまずチェックします。
Do While KEY <> ""は、「変数KEYの中身が空白(ブランク)でない間、Do以下の処理をLoopまで繰り返す」という意味です。 セル番地A11まで連続で読み込むと空欄になるので、そうしたらループを抜け出しなさいということです。 空欄でない間はDo以下の命令を実行していきます。
まずKEYと1つ前に読み込んだsKEYの値をIf文(If KEY <> sKEY Then)で比較します。
比較の評価が真(True)の場合(つまりKEYとsKEYの値が異なる場合)は小計の出力処理が実行されます。 偽(False)の場合(つまりKEYとsKEYの値が等しい場合)は次のステップに移動します。

図6 分類コードの切れ目を調べる
図6 分類コードの切れ目を調べる

次のステップでは現在の金額を小計に加算し、KEYの値をsKEYに保存します。
そして変数iの値を1つ増やし、次のデータを読み込みます。
これらをA列が空欄の行まで、繰り返させます。

次はコメントで説明していた処理を、具体的なコードに置き換えていきましょう。
「小計を出力」の処理は変数Shoukeiの値を、ある場所のセルに書き込む操作となりますので、Cells(j, 5).Value = Shoukeiのような書き方になります。
j行の5列目(E列)に書き込むわけです。
なおjは出力行を示す変数です。当然変数iと同じように宣言して、実行前に値を代入しておく必要があります。
「小計をクリア」は簡単ですね。変数Shoukeiに0(ゼロ)を代入してあげればクリアされます。
「小計に加算」は、まずサブプロシージャの先頭で小計を記憶しておくための変数ShoukeiをCurrency型で宣言しておきます。(データ型については
変数の基本データ型を参照)
金額はi行の2列目(B列)に入っているので、Shoukei = Shoukei + Cells(i, 2).ValueでShoukeiに足しこむことができます。

今まで説明してきた部分をコードでもう一度表してみると、次のようになります。
最後に、もう一度残りの小計を出力させる部分も追加しておきましょう。


Dim KEY As String
Dim sKEY As String
Dim Shoukei As Currency
Dim i As Long
Dim j As Long
i = 2 : j = 2
KEY = Left(Cells(i, 1).Value, 1) 
Do While KEY <> ""
    If KEY <> sKEY Then
        Cells(j, 5).Value = Shoukei
        j = j + 1
        Shoukei = 0
    End If
    Shoukei = Shoukei + Cells(i, 2).Value
    sKEY = KEY
    i = i + 1
    KEY = Left(Cells(i, 1).Value, 1) 
Loop
Shoukei = Shoukei + Cells(i, 2).Value
Cells(j, 5).Value = Shoukei
	
	

では全体のサブプロシージャShuukeiを確認しましょう。

図7
図7 集計マクロ

4.誤りを見つけて修正(デバッグ)する

それでは一応集計マクロらしきものが完成したので、さっそく実行してみましょう。
Sheet1を開いておき、Alt + F8キー でマクロのダイアログボックスを表示し、「Shuukei」マクロを選択して「実行」キーを押してください。
どのような結果になったでしょうか?

図8
図8 集計マクロの実行結果

少しこちらが期待していた結果と違う出力となりました。どうも分類コードA、B、Cに対する小計額が1つづつずれて表示されているようです。
どこがいけなかったのでしょうか?
もう一度小計出力部分のコードを見てみましょう。
1つ前の分類コードと比較して、異なっていたら小計を出力していますが、このコードではそもそも1件目のデータを処理する場合もsKEYと比較しています。
当然1件目を処理する時点ではまだsKEYには何も分類コードは入っていませんから、比較を行えばIf文の評価は真(True)となってしまい、いきなり小計を出力してしまいます。
ここは1件目のデータについては例外的に小計出力処理をスキップするようにしなければならないのでした。
したがって次のようなコードに改めることにします。


If KEY <> sKEY And i > 2 Then
    Cells(j, 5).Value = Shoukei
    j = j + 1
    Shoukei = 0
End If
	
	

小計を出力する条件を追加して、「分類コードが等しくなく、且つ、行番号(変数i)が2より大きい場合」とします。
これで小計出力が正しく表示されるようになりました。
実行結果は次のようになります。

図9
図9 修正後の実行結果

ただこのコードではまだ全体の合計額が出力されていません。合計額は直接セルE5にSUM関数を埋め込んでもいいわけですが、これも練習ですのでマクロで集計しましょう。

図10 合計まで含めた集計マクロ
図10 合計まで含めた集計マクロ

5.動作結果を確認する

変数Goukeiを追加して実行した結果です。
これで意図した結果を得ることができました。

図11 合計処理追加後の実行結果
図11 合計処理追加後の実行結果

実際には、いきなりコードを書いていけば構文エラー(シンタックスエラー)というものにも結構遭遇します。 これは命令の書き方が決められたルール通りではないということですが、この類のエラーは慣れればかなり減少していきます。
問題は論理エラーと呼ばれるもので、作成者の意図した結果が得られない場合です。
これは文法的には間違っていないけど、命令の組み合わせ方、または実行順に問題がある場合に発生することが多いです。
これも慣れるに従い、おおよその原因はすぐに分かるようになりますが、どうしても分からない場合はVBEに備わっているデバッグ機能などを駆使して原因をつきとめなければなりません。
このことが一番難しくてやっかいな訳ですが、逆に楽しい部分でもあります。
特に趣味で作る場合は、こういう現象がたくさんあるほうが面白いものです。
みなさんも期待通りにうまくいかなくても、くさらずに逆にますます興味が沸いてくる段階までVBAに親しんでください。



ページのトップに戻る

 


office boole banner 長崎県長崎市 Office Boole 末永 尚登
E-mail: suenaga@officeboole.com   TEL/FAX: 095-893-6090