初心者のためのExcel エクセルマクロVBA入門-実践:検索処理Find関数(発注書マクロ)
前回で、やりたい処理を日本語で書く。ということをしました。次はいよいよこの処理をVBAで書いて行きたいと思います。その前にもう一度前回までのあらすじ。。。
全体を通してしたいこと
- 自動でCSV在庫表を取込たい
- 発注が必要な商品リストが見たい。
- 商品は取引先ごとに見たい
- 自分で数を入力して発注書を印刷したい
- できればFAXも自動でしたい
とりあえず、実現したいこと
- 会社を選択したら・・・
- 在庫表からその会社の商品をすべて検索して
- 発注が必要な商品をリストに出力する
でしたね。これはなぜ?ど頭のCSVの在庫表の取り込みをしないのか?ということですが、じつはこれ自体は全然大したことがない作業です。だって、ファイル開いてコピペしたらそれで、手作業としては終わりなのですから。。。( ゚д゚)?ハ!
メインはあくまで発注に必要な在庫がどれなのか?発注書が簡単に印刷できるのか?というところなので、まずはそこからVBAでマクロにしてしまうということです。
んで、前回日本語で書いたのがこちら
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' 引数:corpName(会社名) ' 会社名に該当して、発注が必要な商品をリストアップする ' メインの処理 ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Public Sub outputOrderProduct(ByVal corpName As String) ' 会社名を変数に入れよう ' 在庫表シートの会社名の列から会社名を検索 ' 検索結果があれば・・・ ' 検索結果の行の商品在庫と必要在庫数を比較 ' 在庫 <= 必要在庫数 ' その行で必要な情報を発注リスト表へ出力する ' 仕入れ値=価格*0.7の切り捨て ' 最後まで検索しきるまで繰り返す。 End Sub
さて、書いて行きましょう。上から順でいいと思います。
変数に値を入れる。
まずは、「会社名を変数に入れよう」ですね。会社名の変数を宣言して、変数にだいにゅ・・・・( ゚д゚)!
気が付きましたか?そうなんです。この会社名は実はすでに変数に入っています。それが、「引数:corpName」ですね。引数はプロシージャを呼び出す時に一緒に入れることで呼び出したプロシージャ内で使うことができます。
おぉ・・・簡単でした。いや!どうしても俺は変数に入れたい!って人はこうしてもいいですが、処理は無駄です。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' 引数:corpName(会社名) ' 会社名に該当して、発注が必要な商品をリストアップする ' メインの処理 ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Public Sub outputOrderProduct(ByVal corpName As String) Dim kaisya As String ' 会社名を変数に入れよう kaisya = corpName ' 在庫表シートの会社名の列から会社名を検索 ' 検索結果があれば・・・ ' 検索結果の行の商品在庫と必要在庫数を比較 ' 在庫 <= 必要在庫数 ' その行で必要な情報を発注リスト表へ出力する ' 仕入れ値=価格*0.7の切り捨て ' 最後まで検索しきるまで繰り返す。 End Sub
見てわかるとおり、これなら「corpName」を使えばいいじゃん。になります、
検索処理はFind関数
では、次です。会社名を検索しなければいけません。どうしますか?1000回繰り返して検索してもそんなに時間はかかりませんが、スマートなのはFindを使うことです。なぜなら、「そのようにエクセルを操作したのだから」です。Findメソッドの使い方についてはこちらとこちらで紹介しています。マクロの記録なんかして、えらいことになるなよ?ブラザー。ってことです。。
ではその使い方通りにいきましょう。在庫表シートの会社名の列から検索を行うので、在庫表シートに名前を付けましょう。さらに他のシートにも名前を付けておきましょう!
このようになりました。シートのオブジェクト名はプロパティのオブジェクト名を変更すれば簡単です。ここでは、
- 会社名リスト:shtCorprateList
- 在庫表:shtStock
- 発注書テンプレート:shtOrderTemplate
としました。これで、この名前を指定してあげればそのシートを「マクロで操作」できます。
オブジェクトをちゃんと指定すればエクセルマクロは超簡単です!
では、コードです。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' 引数:corpName(会社名) ' 会社名に該当して、発注が必要な商品をリストアップする ' メインの処理 ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Public Sub outputOrderProduct(ByVal corpName As String) '変数の宣言 Dim rngFindResult As Range ' 在庫表シートの仕入れ先の列から会社名を検索 Set rngFindResult = shtStock.Range("F:F").Find(What:=corpName, LookAt:=xlWhole) ' 検索結果があれば・・・ ' 検索結果の行の商品在庫と必要在庫数を比較 ' 在庫 <= 必要在庫数 ' その行で必要な情報を発注リスト表へ出力する ' 仕入れ値=価格*0.7の切り捨て ' 最後まで検索しきるまで繰り返す。 End Sub
はい、できました。たくさん引数はいりませんよ?必要なのは、検索する文字列と、部分一致か完全一致か?のオプションだけです。ですので、ここでは、
Set rngFindResult = shtStock.Range("F:F").Find(What:=corpName, LookAt:=xlWhole)
としています。Whatが検索する内容ですので引数の会社名corpNameが入りますし、LookAtが部分一致か完全一致のオプションです。これには「xlPart」か「xlWhole」かのどちらかが入ります。
※余談
xlPartとかxlWholeとか他にもxlUpとかxlEndなんてのもありますが、これは一体何か?というとエクセルVBAが最初から用意している<「定数」と呼ばれるものです。VBAの本体のプログラム自体はブラックボックスなので、見ることはできませんが、このxlPartやxlWholeには基本的に変数と同じように数値などの値が入っていると思ってください。
定数は値を変えることができません。常に一定です。なので定数ですね。値を変えることができるのは変数です。なので値を変えることができない変数ということが言えます。当然変数と似たように使うことができるのです。変数に定数を入れることもできます。
でもなんでこんなことするのでしょうか?( ゚д゚)?
値が変わらないなら別に1でも0でも普通に書けばいいですよね?何ででしょうか?これは、ちょっと考えればわかりますが、たとえば検索のxlPartとxlWhole、xlPartが1でxlWholeが0とすると、これを覚えてられます?他にもxlEndが4でxlUpが2とか覚えられます?定数がないとずっとこれをプログラムを作る側が「覚えてないといけません」数字をです。数字は数字ですから、部分一致ですとか、上とか下とかとは関連性がありません。
ですので先ほどのプログラムを・・・
Set rngFindResult = shtStock.Range("F:F").Find(What:=corpName, LookAt:=0)
と書いても、正直0が「なんなのか?」ちんぷんかんぷんなのです。ですので、数字を文字に定義づけることで、言葉として数字を利用することができます。これが定数です。
部分一致の場合にはxlPartですよ。と書けば文字なので意味があります。Part、つまりパーツという意味ですからこれが部分一致検索をしていることが見る人には直感的に伝わるのです。
※余談おわり※
余談が長かったですが、続きです。検索結果は上で定義したrngFindResult変数に代入しています。rngFindResultはRangeオブジェクト型ですので、単純に=だけでは代入できません。ちゃんとSetステートメントを使います。
さて、どんどん翻訳しますよー。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' 引数:corpName(会社名) ' 会社名に該当して、発注が必要な商品をリストアップする ' メインの処理 ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Public Sub outputOrderProduct(ByVal corpName As String) '変数の宣言 Dim rngFindResult As Range ' 仕入れ先検索結果格納用 Dim lngStock As Long ' 現在の在庫数 Dim lngNeedOrder As Long ' 必要在庫数 ' 在庫表シートの仕入れ先の列から会社名を検索 Set rngFindResult = shtStock.Range("F:F").Find(What:=corpName, LookAt:=xlWhole) ' 検索結果があれば・・・ If Not rngFindResult Is Nothing Then ' 検索結果の行の商品在庫と必要在庫数を比較 lngStock = shtStock.Cells(rngFindResult.Row, "D").Value lngNeedOrder = shtStock.Cells(rngFindResult.Row, "E").Value ' 在庫 <= 必要在庫数 If lngStock <= lngNeedOrder Then ' その行で必要な情報を発注リスト表へ出力する ' No shtOrderTool.Cells(11, "B").Value = shtStock.Cells(rngFindResult.Row, "A").Value ' 商品名 shtOrderTool.Cells(11, "C").Value = shtStock.Cells(rngFindResult.Row, "B").Value ' 現在庫 shtOrderTool.Cells(11, "D").Value = lngStock ' 仕入れ値=価格*0.7の切り捨て shtOrderTool.Cells(11, "E").Value = Int(shtStock.Cells(rngFindResult.Row, "C").Value * 0.7) End If End If ' 最後まで検索しきるまで繰り返す。 End Sub
はい、オッケーです。さて、一気に増えたのでちょっと混乱でしょうか?でもやっていることは、日本語→VBAに翻訳しただけです。
検索結果があれば・・・→If Not rngFindResult Is Nothing Then
Findの使い方を見た人はわかりますが、Findは検索結果がなければNothingという値を返します。それ以外は検索結果のRangeオブジェクトが戻り値として渡されます。ですので、、
「検索結果があれば」
↓
「検索結果がNothingじゃなければ」
↓
「If Not rngFindResult Is Nothing Then」
という翻訳ですね。If文の使い方はかなり初めにブログに書いていますがNotという演算子とオブジェクトの比較はIsを使うこと。という所に注意してください。こんなんはダメです。
「If rngFindResult != Nothing Then」
検索結果の行の商品在庫と必要在庫数を比較→変数への代入と比較
ここでは、検索結果の在庫数と必要在庫数をそれぞれ変数に代入しています。変数に代入するのでDimステートメントで変数を宣言しています。検索結果はRangeオブジェクトなので、当然プロパティ(状態)として行(Row)と列(column)を持っています。在庫表シートの在庫数の列と、必要在庫数の列は既に分かっているので、ここでは
「shtStock.Cells(rngFindResult.Row, "A").Value」
としているのです、つまり
「検索結果の行」
↓
「rngFindResult.Row」
と翻訳されます。検索結果は先ほど変数に入れましたね。検索結果のの「の」は「.」ドットに翻訳されます。後は行はRowですのでこのようになりますね。
その行で必要な情報を発注リスト表へ出力する→コピーではなく値の代入
必要な情報は検索結果の行が分かった時点で、後は超簡単です。え?コピーすれば・・・いえいえ違います!ここばかりは違います。確かに、エクセルの操作ではコピーとペーストで貼り付けたと思いますが、マクロでは超基本的な方法があります。それが値の代入です。HelloWorldと出力したアレです。つまり
「その行で必要な情報を発注リスト表へ出力する」
↓
「発注リストのセルに検索結果の在庫表の値を代入する」
↓
「shtOrderTool.Cells(11, "C").Value = shtStock.Cells(rngFindResult.Row, "B").Value」
と翻訳するわけです。発注リストのシートの名前はわかっていますよね。そこを座標指定のCellsを使って、その場所へ、見つかった検索結果の行の値を同じようにCellsで指定して代入するわけです。
仕入れ値=価格*0.7の切り捨て→Int関数
仕入れ値は7掛けの切り捨て。ですので、今回はInt関数を使います。きっちり四捨五入ならRound関数を使いましょう。Int関数は引数で与えれた値をなんかごにょごにょしてその整数部分だけを戻り値として返してくれます。ですので。
「価格*0.7の切り捨て」
↓
「Int(shtStock.Cells(rngFindResult.Row, "C").Value * 0.7)」
となるわけです。
さて、後は繰り返しですね。これはまた次回で。。。
今日はここまで!
かしこ