【COBOL学習|豆知識】COBOL報告書作成の秘訣:SUM句のRESET ON句で集計を自在に操る!

はじめに

COBOLで報告書を作成する際、集計機能は欠かせません。特に、特定の項目で集計値をリセットしたり、逆に累計として保持したい場面はよくあります。しかし、デフォルトの集計リセットでは、意図しないタイミングで集計値がクリアされてしまうことも。そんな時、RESET ON句を使いこなせば、集計のタイミングを柔軟に制御し、より高度な報告書作成が可能になります。このTipsでは、RESET ON句の基本的な使い方から、具体的な活用方法、そして現場で役立つ注意点までを、ベテランCOBOL技術者の視点から詳しく解説します。

基礎知識:SUM句とRESET ON句とは?

COBOLのSUM句は、レコードの特定の項目を合計する機能です。例えば、売上金額を合計したい場合などに使用します。

01 WS-SALES-TOTAL PIC 9(10) VALUE 0.
. . .
PROCEDURE DIVISION.
. . .
MOVE WS-ITEM-PRICE TO WS-SALES-TOTAL
. . .

しかし、このSUM句だけでは、集計値は通常、対応するAT END OF句やCONTROL BREAK句のタイミングで自動的にリセットされてしまいます。

ここで登場するのがRESET ON句です。この句は、SUM句による集計値を「いつリセットするか」を明示的に指定するためのものです。
RESET ON句を使わない場合、集計値は通常、そのSUM句が関連付けられているコントロールブレークの切れ目でリセットされます。
しかし、例えば「商品コードごとに売上を集計し、さらに最終的な総計も表示したい」といった場合、商品コードの切れ目でリセットされると、総計が正しく計算できません。

RESET ON句を使うと、このような累計を保持したい場合に、より上位のキー(例えば、部門コードや最終的な総計)が切れるまで集計値をリセットしないように指示できます。

実装/解決策:RESET ON句の具体的な使い方

RESET ON句は、SUM句の直後に記述します。

1. 特定のキーでリセットする場合

特定のコントロールブレークのタイミングで集計値をリセットしたい場合は、そのコントロールブレークのキーを指定します。

例:商品コード(ITEM-CODE)ごとに売上を集計し、商品コードが切り替わるタイミングでリセットしたい場合。

01 REPORT-RECORD.
05 ITEM-CODE PIC X(5).
05 ITEM-PRICE PIC 9(7).
05 ITEM-SALES-SUM PIC 9(10) VALUE 0.

. . .

PROCEDURE DIVISION.
. . .

  • ITEM-SALES-SUM を ITEM-CODE ごとに集計し、ITEM-CODE が変わるたびにリセット

SUM ITEM-PRICE TO ITEM-SALES-SUM RESET ON ITEM-CODE.
. . .

この場合、`ITEM-CODE`が変わる(コントロールブレークが発生する)たびに`ITEM-SALES-SUM`は0にリセットされます。

2. 累計を保持したい場合 (RESET ON FINAL)

より上位のキーや、最終的な総計まで集計値を保持したい場合は、`FINAL`を指定します。これが、参考本文で示されている例です。

例:商品コードごとに売上を集計しつつ、最終的な総計も表示したい場合。

01 WS-TOTAL-SALES PIC 9(10) VALUE 0.
. . .
PROCEDURE DIVISION.
. . .

  • 売上金額を累計し、最終的な総計までリセットしない

SUM WS-ITEM-PRICE TO WS-TOTAL-SALES RESET ON FINAL.
. . .

この`RESET ON FINAL.`という記述は、「プログラムの終了時(または報告書の最終ページ)まで、この`WS-TOTAL-SALES`の値はリセットせずに保持し続ける」という意味になります。これにより、プログラム全体での総計を簡単に取得できます。

サンプルプログラム

ここでは、`RESET ON FINAL`句を使った簡単なサンプルプログラムを示します。このプログラムは、部門コードと商品コードごとに売上を集計し、さらに最終的な総売上も算出します。

IDENTIFICATION DIVISION.
PROGRAM-ID. RESETDEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DATA-REC.
05 WS-DEPT-CODE PIC X(2).
05 WS-ITEM-CODE PIC X(3).
05 WS-SALES-AMT PIC 9(6).

01 WS-REPORT-LINE.
05 RP-DEPT-CODE PIC X(2).
05 RP-ITEM-CODE PIC X(3).
05 RP-ITEM-SALES PIC Z(6)9.
05 FILLER PIC X(5) VALUE SPACES.
05 RP-DEPT-SALES PIC Z(8)9.
05 FILLER PIC X(5) VALUE SPACES.
05 RP-FINAL-SALES PIC Z(10)9.

01 WS-SUM-AREA.
05 WS-DEPT-SALES PIC 9(8) VALUE 0.
05 WS-FINAL-SALES PIC 9(10) VALUE 0.

PROCEDURE DIVISION.
MAIN-PROCEDURE.
PERFORM 1000-INITIALIZE.
PERFORM 2000-PROCESS-RECORDS
UNTIL WS-EOF = ‘Y’.
PERFORM 3000-FINALIZE.
STOP RUN.

1000-INITIALIZE.
DISPLAY “— データ処理開始 —“.

  • サンプルデータの準備 (実際はREAD文などでファイルから読み込む)

MOVE ‘A1’ TO WS-DEPT-CODE.
MOVE ‘X01’ TO WS-ITEM-CODE.
MOVE 1000 TO WS-SALES-AMT.
PERFORM 2100-PROCESS-RECORD.

MOVE ‘A1’ TO WS-DEPT-CODE.
MOVE ‘X02’ TO WS-ITEM-CODE.
MOVE 1500 TO WS-SALES-AMT.
PERFORM 2100-PROCESS-RECORD.

MOVE ‘A1’ TO WS-DEPT-CODE.
MOVE ‘X01’ TO WS-ITEM-CODE.
MOVE 500 TO WS-SALES-AMT.
PERFORM 2100-PROCESS-RECORD.

MOVE ‘B2’ TO WS-DEPT-CODE.
MOVE ‘Y01’ TO WS-ITEM-CODE.
MOVE 2000 TO WS-SALES-AMT.
PERFORM 2100-PROCESS-RECORD.

MOVE ‘B2’ TO WS-DEPT-CODE.
MOVE ‘Y02’ TO WS-ITEM-CODE.
MOVE 2500 TO WS-SALES-AMT.
PERFORM 2100-PROCESS-RECORD.

MOVE ‘A1’ TO WS-DEPT-CODE. > データ終端の印 (実際はEOFフラグなど)
MOVE ‘Z99’ TO WS-ITEM-CODE.
MOVE 0 TO WS-SALES-AMT.
MOVE ‘Y’ TO WS-EOF.

2000-PROCESS-RECORDS.

  • ここではサンプルデータなので、実際にはREAD文などでデータを取得
  • 例: READ INPUT-FILE INTO WS-DATA-REC AT END MOVE ‘Y’ TO WS-EOF.

. . . > READ処理を想定

2100-PROCESS-RECORD.
部門コードが変わったら部門集計をリセット
IF WS-DEPT-CODE NOT = PREV-WS-DEPT-CODE
AND PREV-WS-DEPT-CODE NOT = SPACES
PERFORM 2200-WRITE-REPORT-LINE
MOVE 0 TO WS-DEPT-SALES > 部門集計をリセット
END-IF.

  • 部門ごとの売上を集計 (部門コードが変わっても累計を続ける)

ADD WS-SALES-AMT TO WS-DEPT-SALES.

  • 最終的な総売上も集計 (RESET ON FINAL でリセットされない)

SUM WS-SALES-AMT TO WS-FINAL-SALES RESET ON FINAL.

  • 次のレコードのために現在の部門コードを保持

MOVE WS-DEPT-CODE TO PREV-WS-DEPT-CODE.

2200-WRITE-REPORT-LINE.
MOVE WS-DEPT-CODE TO RP-DEPT-CODE.
MOVE WS-ITEM-CODE TO RP-ITEM-CODE.
MOVE WS-SALES-AMT TO RP-ITEM-SALES. > 個別売上
MOVE WS-DEPT-SALES TO RP-DEPT-SALES. > 部門累計
MOVE WS-FINAL-SALES TO RP-FINAL-SALES. > 総累計
DISPLAY RP-DEPT-CODE RP-ITEM-CODE RP-ITEM-SALES RP-DEPT-SALES RP-FINAL-SALES.

3000-FINALIZE.

  • 最後の部門の集計結果を表示

IF PREV-WS-DEPT-CODE NOT = SPACES
PERFORM 2200-WRITE-REPORT-LINE
END-IF.
DISPLAY “— 処理完了 —“.
DISPLAY “最終総売上: ” WS-FINAL-SALES.

  • 補助変数(前回の部門コードを保持するため)

01 PREV-WS-DEPT-CODE PIC X(2) VALUE SPACES.
01 WS-EOF PIC X(1) VALUE ‘N’.

このサンプルでは、`SUM WS-SALES-AMT TO WS-FINAL-SALES RESET ON FINAL.` の部分が、最終的な総売上をプログラム終了まで累計し続ける役割を担っています。

応用・注意点

  • `RESET ON`句の指定順序: `RESET ON`句は、`SUM`句の機能と密接に関連しています。どのコントロールブレークでリセットしたいのか、あるいは累計したいのかを明確に意識して記述することが重要です。
  • `FINAL`の強力さ: `RESET ON FINAL`は非常に便利ですが、その分、意図しないタイミングでのリセットを防ぐために、他の`RESET ON`句やコントロールブレークのロジックとの整合性をよく確認する必要があります。
  • パフォーマンスへの影響: 通常、`RESET ON`句の使用がパフォーマンスに与える影響は軽微です。しかし、極端に大量のデータを処理する場合や、複雑なネスト構造を持つSUM句を使用する場合は、念のためテストで確認することをお勧めします。
  • 可読性: 複雑な集計ロジックになるほど、コードの可読性が重要になります。`RESET ON`句を効果的に使うことで、集計ロジックを簡潔に表現できる場合がありますが、逆に多用しすぎると理解しにくくなることもあります。コメントを適切に記述するなど、可読性を意識したコーディングを心がけましょう。
  • `EVALUATE`文との組み合わせ: `EVALUATE`文の中で複数の`SUM`句を使用し、それぞれに異なる`RESET ON`句を指定すると、より複雑な集計処理も実現できます。

`RESET ON`句をマスターすれば、COBOLでの報告書作成の幅が格段に広がります。ぜひ、日々の開発で活用してみてください。

コメント

タイトルとURLをコピーしました