- ベストアンサー
グループごとの集計、全体の集計について
いつもお世話になっております。 VBAでの課題で行き詰ってしまったので、どなたかお力をお貸しいただけないでしょうか? コードの効率のよい書き方が分からないため 非常に長くなってしまいますが、すべて書かせていただきます。 課題は A グループ1 商品1 300000 A グループ1 商品2 460000 A グループ2 商品1 120000 A グループ2 商品2 80000 A グループ3 商品3 71000 B グループ1 商品1 200000 B グループ1 商品2 208000 B グループ2 商品1 2300 となっている表を A グループ1 商品1 300000 A グループ1 商品2 460000 グループ1 760000 A グループ2 商品1 120000 A グループ2 商品2 80000 グループ2 200000 A グループ3 商品3 71000 グループ3 71000 支店A 1031000 B グループ1 商品1 200000 B グループ1 商品2 208000 グループ1 408000 B グループ2 商品1 2300 グループ2 2300 支店B 410300 合計(A+B) 1441300 このように、グループが変わるとグループ合計を出し、 支店名が変わると支店合計を出し、 最終的にすべての合計を出力するコーディングをしています。 今回は配列変数を使わないということなのですが 以下のような流れで考えました '変数の宣言 Dim X As String Dim Y As String Dim siten_A As String '支店名 Dim siten_B As String Dim kubun_A As String '区分 Dim kubun_B As String Dim syohin_A As String '商品名 Dim syohin_B As String Dim kingaku_A As Long '金額 Dim kingaku_B As Long Dim k_goukei As Long '区分合計 Dim s_goukei As Long '支店合計 Dim goukei As Long '合計 'ファイルを開く Open "C:\My Documents\INFILE.txt" For Input As #1 Open "C:\My Documents\OUTFILE.txt" For Output As #2 '1行目を読み込み、変数に格納 Line Input #1, X siten_A = Left(X, 10) kubun_A = Mid(X, 11, 10) kingaku_A = Right(X, 8) syohin_A = Mid(X, 21, 15) Do Until EOF(1) '2行目以降を読み込み変数に格納 Line Input #1, Y siten_B = Left(Y, 10) kubun_B = Mid(Y, 11, 10) kingaku_B = Right(Y, 8) syohin_B = Mid(Y, 21, 15) If siten_A = siten_B And kubun_A = kubun_B Then Print #2, siten_A & kubun_A & syohin_A & kingaku_A k_goukei = kingaku_A + kingaku_B kingaku_A = kingaku_B siten_A = siten_B kubun_A = kubun_B syohin_A = syohin_B ElseIf siten_A = siten_B And kubun_A <> kubun_B Then Print #2, siten_A & kubun_A & syohin_A & kingaku_A s_goukei = s_goukei + k_goukei k_goukei = k_goukei Print #2, k_goukei siten_A = siten_B kubun_A = kubun_B syohin_A = syohin_B kingaku_A = kingaku_B Else Print #2, siten_A & kubun_A & syohin_A & kingaku_A Print #2, k_goukei s_goukei = s_goukei + k_goukei Print #2, s_goukei siten_A = siten_B kubun_A = kubun_B syohin_A = syohin_B kingaku_A = kingaku_B End If Loop Print #2, siten_A & kubun_A & syohin_A & kingaku_A k_goukei = k_goukei + kingaku_A Print #2, k_goukei s_goukei = s_goukei + k_goukei Print #2, s_goukei goukei = goukei + s_goukei Print #2, goukei Close #1 Close #2 End Sub となっています。 これを実行すると、各レコードを出力した後に 合計を出したいのですが 各レコードの金額が、一つ前の金額に足されたものになっており 期待通りの出力ができません。 前半で間違っているため、後半の支店合計や全体の合計も 変わってきてしまい、どこをなおせばよいかわからない状態です。 VBは初心者なので、長くなってもかまわないので 教えていただければと思います。 長くなりましたが、よろしくお願いします。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
一見したところ計のクリアーとか計算の仕方に間違いがあるようです。 折角質問者自身で書いたコードですからそれを修正するとして、 (前提条件) 計算違いを除いて上手く動作していること。 以下の▲印は削除して、■を追加すること。 '------------------------------------------------ Sub org() '変数の宣言 Dim X As String Dim Y As String Dim siten_A As String '支店名 Dim siten_B As String Dim kubun_A As String '区分 Dim kubun_B As String Dim syohin_A As String '商品名 Dim syohin_B As String Dim kingaku_A As Long '金額 Dim kingaku_B As Long Dim k_goukei As Long '区分合計 Dim s_goukei As Long '支店合計 Dim goukei As Long '合計 'ファイルを開く Open "C:\My Documents\INFILE.txt" For Input As #1 Open "C:\My Documents\OUTFILE.txt" For Output As #2 '1行目を読み込み、変数に格納 Line Input #1, X siten_A = Left(X, 10) kubun_A = Mid(X, 11, 10) kingaku_A = Right(X, 8) syohin_A = Mid(X, 21, 15) Do Until EOF(1) '2行目以降を読み込み変数に格納 Line Input #1, Y siten_B = Left(Y, 10) kubun_B = Mid(Y, 11, 10) kingaku_B = Right(Y, 8) syohin_B = Mid(Y, 21, 15) If siten_A = siten_B And kubun_A = kubun_B Then Print #2, siten_A & kubun_A & syohin_A & kingaku_A ▲削除▲ k_goukei = kingaku_A + kingaku_B ■k_goukei = k_goukei + kingaku_A kingaku_A = kingaku_B siten_A = siten_B kubun_A = kubun_B syohin_A = syohin_B ElseIf siten_A = siten_B And kubun_A <> kubun_B Then Print #2, siten_A & kubun_A & syohin_A & kingaku_A ■k_goukei = k_goukei + kingaku_A s_goukei = s_goukei + k_goukei ▲削除▲k_goukei = k_goukei ■k_goukei = 0 Print #2, k_goukei siten_A = siten_B kubun_A = kubun_B syohin_A = syohin_B kingaku_A = kingaku_B Else Print #2, siten_A & kubun_A & syohin_A & kingaku_A ■k_goukei = k_goukei + kingaku_A Print #2, k_goukei s_goukei = s_goukei + k_goukei Print #2, s_goukei ■goukei = goukei + s_goukei ■k_goukei = 0 ■s_goukei = 0 siten_A = siten_B kubun_A = kubun_B syohin_A = syohin_B kingaku_A = kingaku_B End If Loop Print #2, siten_A & kubun_A & syohin_A & kingaku_A k_goukei = k_goukei + kingaku_A Print #2, k_goukei s_goukei = s_goukei + k_goukei Print #2, s_goukei goukei = goukei + s_goukei Print #2, goukei Close #1 Close #2 End Sub '-------------------------------------------------- ちょっと試して見てください。 上手くいきましたら同じような部分はひとつで済むようなコードにトライしてみるのも勉強になると思います。
その他の回答 (3)
- nda23
- ベストアンサー率54% (777/1415)
>各項目は字数と(桁数)が決まっており これは質問者さんしか知らない仕様なので、当方は空白で区切って みただけです。Split関数は第1引数を第2引数で分離し、分離後の 配列を返します。これを受け取る変数はVariant型である必要がある ため、「Dim 項目 As Varint」という定義にしてあります。 項目の位置と長さが分かっているならMidでやっても良いです。 >また、IFの分岐で IfはEOFの判断以外に使用していませんが? >支店=支店、区分=区分の場合 この状態ではブレークは発生せず、最も内側のループを繰り返し 実行します。要するに、明細行の出力、小計の加算、次のデータの 読み込みが行われます。 ★最も内側のLoopのUntil条件のいずれにも該当しない。 >支店=支店、区分<>区分の場合 この条件になると、最も内側のループを抜ます。つまり、 「小(区分)ブレークの後処理」が実行されます。 ★最も内側のLoopのUntil条件の「kubun_A <> 項目(1)」に該当する。 Loop Until の後ろの部分が条件式になっています。 ブレークレベルが3なら、Do~Loopのネストも3レベルにならないと おかしいのです。掲題のプログラムではDo~Loopはネストしておらず、 If文で無理やり制御しているようですが、どのような考え方に基いて 作られたか理解できません。プロの場合、先ず考え方を整理し、文書化 してから複数人の検証(レビューと言う)を受けます。ここで、仕様や 考え方の間違いを正し、それからプログラムします。 そういう観点からすると、Do~Loopが3重になっていない段階で、 残念ながら却下です。詳しく調べれば正しい結果が得られるように なっているとしても、「詳しく調べなければ分からない」という 時点で、品質上アウトです。
お礼
回答いただきありがとうございます。 Split関数、今後使えるようになりたいと思います。 理解しづらいコーディングになってしまい申し訳ありません。 プログラミングを始めてまだ1ヶ月なので どういったものが品質上位いいかすら分からない状態なのですが 今後、少しでも無駄のないものを作っていきたいと思います。
- nda23
- ベストアンサー率54% (777/1415)
先ず、考え方から整理します。 (1)データは1行につき、支店名、区分、商品名、金額からなり、 空白1文字(ここ大事)で区切られている。 (2)区分が変わった(ブレークと言う)ら小計を出力する。 (3)支店名が変わったら中計を出力する。 (4)最後に全合計を出力する。 ブレーク基準は3個あります。大きい方からEOF、支店名、区分です。 また、合計データも3レベル必要ですね。以下は要点を記述しました。 '** いきなりEOFなら終了する If EOF(1) Then Close MsgBox "ファイルが空です" Exit Sub End If Dim 項目 As Variant '1行の項目の集団(漢字の変数名はOK) Line Input #1, X '先頭データを読み込む 項目 = Split(X, " ") '空白で区切る '★先ず最も外側のブレークを記述します Do '==ここは中(支店名)ブレークの初期設定です。 s_goukei = 0 '支店合計を初期化 siten_A = 項目(0) '支店用ブレークキー(区切った項目の最初) '★支店名ブレークを記述します Do '== ここは小(区分)ブレークの初期設定です。 k_goukei = 0 '区分合計を初期化 kubun_A = 項目(1) 'kubun用ブレークキー(区切った項目の2番目) '★区分ブレークを記述します Do '明細を出力 Print #2, siten_A, kubun_A, 項目(2), 項目(3) '小計を計算する k_goukei = k_goukei + CLng(項目(3)) '次のデータを処理する If Not EOF(1) Then Line Input #1, X 'データを読み込む 項目 = Split(X, " ") '空白で区切る End If Until EOF(1) Or siten_A <> 項目(0) Or kubun_A <> 項目(1) '==ここは小(区分)ブレークの後処理です。 Print #2, kubun_A, k_goukei s_goukei = s_goukei + k_goukei '中計に小計を加算 Until EOF(1) Or siten_A <> 項目(0) '==ここは中(支店名)ブレークの後処理です。 Print #2, siten_A, s_goukei goukei = goukei + s_goukei '全計に中計を加算 Until EOF(1) '==大(EOF)ブレークの後処理です。 Print #2,"合計", goukei Close
補足
回答いただき、ありがとうございます。 >Dim 項目 As Variant '1行の項目の集団(漢字の変数名はOK) の意味がよく分からないのですが、 各項目は字数と(桁数)が決まっており siten_B = Left(Y, 10) kubun_B = Mid(Y, 11, 10) kingaku_B = Right(Y, 8) syohin_B = Mid(Y, 21, 15) のように位置を指定して、変数に代入しているのですが それでは駄目なのでしょうか? また、IFの分岐で 支店=支店、区分=区分の場合 支店=支店、区分<>区分の場合 その他の場合 といったようにはできないでしょうか? よろしく尾お願いいたします。
- nattocurry
- ベストアンサー率31% (587/1853)
> 課題は > A グループ1 商品1 300000 > A グループ1 商品2 460000 > A グループ2 商品1 120000 > A グループ2 商品2 80000 > A グループ3 商品3 71000 > B グループ1 商品1 200000 > B グループ1 商品2 208000 > B グループ2 商品1 2300 > > となっている表を > A グループ1 商品1 300000 > A グループ1 商品2 460000 > グループ1 760000 > A グループ2 商品1 120000 > A グループ2 商品2 80000 > グループ2 200000 > A グループ3 商品3 71000 > グループ3 71000 > 支店A 1031000 > B グループ1 商品1 200000 > B グループ1 商品2 208000 > グループ1 408000 > B グループ2 商品1 2300 > グループ2 2300 > 支店B 410300 > 合計(A+B) 1441300 のような感じで、 > 各レコードの金額が、一つ前の金額に足されたものになっており期待通りの出力ができません。 この結果も、具体的に提示すると、回答しやすいです。
補足
ご指摘ありがとうございます。 実際は、もっとレコード数が多く簡略化して書いたので 本来ですと A グループ1 商品1 300000 A グループ1 商品2 460000 グループ1 760000 A グループ2 商品1 120000 A グループ2 商品2 80000 グループ2 200000 支店A 960000 B グループ1 商品1 200000 B グループ1 商品2 208000 B グループ1 商品3 75000 B グループ1 商品4 60000 グループ1 135000 B グループ2 商品1 005000 B グループ2 商品2 030000 グループ2 35000 支店B 1130000 合計 1130000 という状態になっています。 支店Bグループ1の合計であったり 最後の合計であったりが、異なる数字が出力されています。
お礼
非常に分かりやすく教えていただいてありがとうございます。 s_goukeiやk_goukeiを「0」にするのが必要だったのですね!! 教えていただいたおかげで、無事期待通りの出力ができました。 最後にありますように、少しでもスマートなコードを作れるように がんばりたいと思います。 本当にありがとうございました。