【COBOL学習|実務向け】報告書作成の要!CONTROLS句(集計キー)を使いこなす!

はじめに:なぜCONTROLS句が重要なのか?

COBOLで報告書を作成する際、データを集計して階層的に表示したい場面は非常に多いものです。例えば、部署別、課別といったようなグルーピングを行い、それぞれの集計値を出力したい場合です。このような集計処理を効率的に行うための強力な機能が `CONTROLS` 句、通称「集計キー」です。

`CONTROLS` 句を指定することで、COBOLプログラムは自動的に「コントロール切れ(Control Break)」を検知し、指定した項目の値が変わるたびに、あらかじめ定義しておいたサブルーチン(通常は集計行を出力する処理)を呼び出してくれます。これにより、開発者は面倒な条件分岐によるコントロール切れの判定や、集計処理の記述を大幅に削減でき、生産性が向上します。

特に、複雑な階層構造を持つ報告書や、頻繁に集計ロジックを変更する必要がある場合に、`CONTROLS` 句は真価を発揮します。

CONTROLS句の基礎知識

`CONTROLS` 句は、`PROCEDURE DIVISION` の中で、`REPORT SECTION` の前に記述される、報告書作成機能(Report Writer)における重要な句です。

  • 集計キー(Control Key): `CONTROLS` 句で指定する項目(データ項目名)のことです。これらの項目は、報告書の階層構造と集計の基準を定義します。
  • コントロール切れ(Control Break): 指定された集計キーの値が、前のレコードから変化した状態を指します。例えば、集計キーに `DEPT-CODE` を指定した場合、`DEPT-CODE` の値が「A01」から「A02」に変わった瞬間にコントロール切れが発生します。
  • 階層構造: `CONTROLS` 句に複数の集計キーを指定した場合、その順序が階層構造を決定します。左側にある項目ほど上位の階層となり、その項目でコントロール切れが発生すると、それより右側にある集計キーのコントロール切れはリセットされます。
  • FINAL: `CONTROLS` 句の最も左側に指定できる特殊なキーワードです。`FINAL` を指定すると、レコード処理の最後に一度だけコントロール切れが発生したとみなされます。これは、最終的な総計行を出力する際によく使用されます。

CONTROLS句の実装と解説

`CONTROLS` 句は、`PROCEDURE DIVISION` の冒頭で、報告書定義(`REPORT SECTION`)の前に記述します。

PROCEDURE DIVISION.
DECLARATIVES.
INPUT-OUTPUT SECTION.
FILE SECTION.
WORKING-STORAGE SECTION.
REPORT SECTION.
RD REPORT-NAME.

  • ここにPAGE-HEADING, CONTROL-HEADING, DETAIL, CONTROL-FOOTING, PAGE-FOOTINGなどを記述

END DECLARATIVES.

  • —– CONTROLS句の記述 —–

CONTROLS ARE FINAL, DEPT-CODE, SECT-CODE.

  • —————————–

MAIN-PROCESSING.
PERFORM 1000-INITIALIZE
PERFORM 2000-PROCESS-RECORDS
PERFORM 3000-TERMINATE
GOBACK.

  • … その他のサブルーチン …

上記の例では、`CONTROLS ARE FINAL, DEPT-CODE, SECT-CODE.` と記述されています。これは、以下の意味を持ちます。

1. `SECT-CODE`: 最も細かい階層の集計キーです。`SECT-CODE` の値が変わるたびに、`CONTROL-FOOTING SECT-CODE` セクションが実行されます。
2. `DEPT-CODE`: 中間の階層の集計キーです。`DEPT-CODE` の値が変わると、まず `SECT-CODE` のコントロール切れが発生し、その後 `DEPT-CODE` のコントロール切れが発生します。`CONTROL-FOOTING DEPT-CODE` セクションが実行されます。
3. `FINAL`: 最上位の集計キーであり、データ処理の最後に一度だけ実行されます。`CONTROL-FOOTING FINAL` セクションが実行され、最終的な総計などを出力します。

コントロール・フッティング (`CONTROL-FOOTING`):
`CONTROLS` 句で指定した集計キーごとに、その階層の集計処理や小計行を出力するためのセクションを定義します。例えば、`CONTROL-FOOTING SECT-CODE.` のように記述し、そのセクション内に集計処理を記述します。

サンプルプログラム

ここでは、商品マスタを想定し、部門コード (`DEPT-CODE`) と商品コード (`ITEM-CODE`) で集計する簡単な例を示します。

IDENTIFICATION DIVISION.
PROGRAM-ID. REPORT-CONTROLS-SAMPLE.
AUTHOR. YOUR-NAME.

DATA DIVISION.
FILE SECTION.
FD INPUT-FILE
RECORD CONTAINS 80 CHARACTERS
DATA RECORD IS INPUT-RECORD.
01 INPUT-RECORD.
05 DEPT-CODE PIC X(05).
05 ITEM-CODE PIC X(10).
05 ITEM-NAME PIC X(30).
05 PRICE PIC 9(07)V99.
05 QUANTITY PIC 9(05).
05 AMOUNT PIC 9(09)V99.

FD OUTPUT-REPORT
RECORD CONTAINS 132 CHARACTERS
DATA RECORD IS REPORT-LINE.
01 REPORT-LINE PIC X(132).

WORKING-STORAGE SECTION.
01 WS-SUMMARY-DATA.
05 WS-DEPT-TOTAL-AMOUNT PIC 9(11)V99 VALUE 0.
05 WS-FINAL-TOTAL-AMOUNT PIC 9(12)V99 VALUE 0.

REPORT SECTION.
RD SALES-REPORT
CONTROLS ARE FINAL, DEPT-CODE.
01 PAGE-HEADING.
05 LINE-01 PIC X(132) VALUE ALL ‘-‘.
05 LINE-02 PIC X(40) VALUE ‘部門別売上報告書’.
05 LINE-03 PIC X(55) VALUE ‘作成日:’.
05 LINE-03-DATE PIC X(08) VALUE ‘YYYYMMDD’. > 自動で日付が入る
05 LINE-04 PIC X(132) VALUE ALL ‘-‘.
05 LINE-05 PIC X(05) VALUE ‘部門’.
05 LINE-05-ITEM PIC X(10) VALUE ‘商品コード’.
05 LINE-05-NAME PIC X(30) VALUE ‘商品名’.
05 LINE-05-PRICE PIC X(10) VALUE ‘単価’.
05 LINE-05-QTY PIC X(05) VALUE ‘数量’.
05 LINE-05-AMT PIC X(10) VALUE ‘金額’.
05 LINE-06 PIC X(132) VALUE ALL ‘-‘.

01 CONTROL-HEADING DEPT-CODE.
05 LINE-10 PIC X(132) VALUE ALL ‘ ‘.
05 LINE-11 PIC X(05) PIC X(05) FROM DEPT-CODE.
05 LINE-11-TOT PIC X(20) VALUE ‘ 部門合計:’.
05 LINE-11-AMT PIC ZZZZZZZZZ.99 FROM WS-DEPT-TOTAL-AMOUNT.

01 DETAIL-LINE.
05 LINE-20 PIC X(05) PIC X(05) FROM DEPT-CODE.
05 LINE-20-ITEM PIC X(10) PIC X(10) FROM ITEM-CODE.
05 LINE-20-NAME PIC X(30) PIC X(30) FROM ITEM-NAME.
05 LINE-20-PRICE PIC X(10) PIC ZZZZZZZZ.99 FROM PRICE.
05 LINE-20-QTY PIC X(05) PIC ZZZZ9 FROM QUANTITY.
05 LINE-20-AMT PIC X(10) PIC ZZZZZZZZ.99 FROM AMOUNT.

01 CONTROL-FOOTING DEPT-CODE.
05 LINE-30 PIC X(132) VALUE ALL ‘ ‘.
05 LINE-31 PIC X(50) VALUE ‘— 部門合計 —‘.
05 LINE-31-AMT PIC ZZZZZZZZZ.99 FROM WS-DEPT-TOTAL-AMOUNT.

01 CONTROL-FOOTING FINAL.
05 LINE-40 PIC X(132) VALUE ALL ‘ ‘.
05 LINE-41 PIC X(50) VALUE ‘=== 総計 ==’.
05 LINE-41-AMT PIC ZZZZZZZZZZ.99 FROM WS-FINAL-TOTAL-AMOUNT.

PROCEDURE DIVISION.
DECLARATIVES.
INPUT-OUTPUT SECTION.
FILE SECTION.
FD INPUT-FILE
RECORD CONTAINS 80 CHARACTERS
DATA RECORD IS INPUT-RECORD.
01 INPUT-RECORD.
05 DEPT-CODE PIC X(05).
05 ITEM-CODE PIC X(10).
05 ITEM-NAME PIC X(30).
05 PRICE PIC 9(07)V99.
05 QUANTITY PIC 9(05).
05 AMOUNT PIC 9(09)V99.

FD OUTPUT-REPORT
RECORD CONTAINS 132 CHARACTERS
DATA RECORD IS REPORT-LINE.
01 REPORT-LINE PIC X(132).
END DECLARATIVES.

  • —– CONTROLS句の記述 —–

CONTROLS ARE FINAL, DEPT-CODE.

  • —————————–

MAIN-PROCESSING.
PERFORM 1000-INITIALIZE
PERFORM 2000-PROCESS-RECORDS
PERFORM 3000-TERMINATE
GOBACK.

1000-INITIALIZE.
OPEN INPUT INPUT-FILE.
OPEN OUTPUT OUTPUT-REPORT.
MOVE FUNCTION CURRENT-DATE TO LINE-03-DATE. > 現在の日付を設定

  • 最初のページヘッダーを出力

WRITE REPORT-LINE FROM PAGE-HEADING
AFTER ADVANCING PAGE.

2000-PROCESS-RECORDS.
PERFORM UNTIL EOF-INPUT
READ INPUT-FILE
AT END MOVE ‘Y’ TO EOF-INPUT
NOT AT END

  • DETAIL行のデータをMOVE

MOVE DEPT-CODE TO DETAIL-LINE(1:5)
MOVE ITEM-CODE TO DETAIL-LINE(6:10)
MOVE ITEM-NAME TO DETAIL-LINE(16:30)
MOVE PRICE TO DETAIL-LINE(46:10)
MOVE QUANTITY TO DETAIL-LINE(56:5)
MOVE AMOUNT TO DETAIL-LINE(61:10)

  • 集計処理 (各レコード読み込み時)

ADD AMOUNT TO WS-DEPT-TOTAL-AMOUNT
ADD AMOUNT TO WS-FINAL-TOTAL-AMOUNT

  • DETAIL行を出力

WRITE REPORT-LINE FROM DETAIL-LINE
END-READ
END-PERFORM.

3000-TERMINATE.

  • 最後のレコード処理後、自動的にFINALのCONTROL-FOOTINGが実行される
  • 個別にCLOSE処理を実行

CLOSE INPUT-FILE.
CLOSE OUTPUT-REPORT.

EOF-INPUT PIC X(01) VALUE ‘N’. > ファイル終端フラグ

END PROGRAM REPORT-CONTROLS-SAMPLE.

サンプルコードの解説:

  • `RD SALES-REPORT CONTROLS ARE FINAL, DEPT-CODE.` : 報告書 `SALES-REPORT` の定義であり、集計キーとして `DEPT-CODE` と `FINAL` を指定しています。
  • `PAGE-HEADING`: 報告書のページヘッダー部分を定義します。
  • `CONTROL-HEADING DEPT-CODE`: `DEPT-CODE` が変わるたびに(コントロール切れ発生時)出力されるヘッダーです。ここでは部門コードと部門合計の表示を試みていますが、実際には `CONTROL-FOOTING` で合計を出力するのが一般的です。この例では `CONTROL-FOOTING` をメインに記述しています。
  • `DETAIL-LINE`: 各レコードの詳細行を定義します。
  • `CONTROL-FOOTING DEPT-CODE`: `DEPT-CODE` のコントロール切れが発生した際に実行されるセクションです。部門ごとの合計行 (`WS-DEPT-TOTAL-AMOUNT`) を出力します。
  • `CONTROL-FOOTING FINAL`: データ処理の最後に一度だけ実行されるセクションです。最終的な総計行 (`WS-FINAL-TOTAL-AMOUNT`) を出力します。
  • `1000-INITIALIZE`: ファイルのオープン、ページヘッダーの出力を行います。
  • `2000-PROCESS-RECORDS`: ファイルを読み込み、各レコードの集計処理(`WS-DEPT-TOTAL-AMOUNT` と `WS-FINAL-TOTAL-AMOUNT` への加算)と詳細行の出力を行います。
  • `3000-TERMINATE`: ファイルのクローズを行います。
  • `WS-DEPT-TOTAL-AMOUNT` および `WS-FINAL-TOTAL-AMOUNT`: 集計値を保持するための作業領域です。これらの変数は、`CONTROL-FOOTING` セクションで参照され、合計値の出力に使用されます。

注意: 上記サンプルでは、`INPUT-FILE` から読み込んだ `AMOUNT` を `WS-DEPT-TOTAL-AMOUNT` および `WS-FINAL-TOTAL-AMOUNT` に加算していますが、これは各レコード処理時に行う集計の例です。`CONTROLS` 句は、この集計処理を「いつ」実行するかを制御するものであり、集計ロジック自体は `PROCEDURE DIVISION` の中で別途記述する必要があります。

応用・注意点

  • 集計キーの順序: `CONTROLS` 句で指定する項目の順序は非常に重要です。左側が上位の階層、右側が下位の階層となります。例えば、`CONTROLS ARE DEPT-CODE, SECT-CODE.` と指定した場合、`SECT-CODE` の変化で `CONTROL-FOOTING SECT-CODE` が実行され、その後 `DEPT-CODE` の変化で `CONTROL-FOOTING DEPT-CODE` が実行されます。
  • 集計キーのデータ型: 集計キーとして指定するデータ項目は、通常、`PIC X` や `PIC 9` のような単純なデータ型である必要があります。グループ項目や `REDEFINES` された項目を集計キーに指定することはできません。
  • `CONTROL-FOOTING` の実行タイミング: `CONTROL-FOOTING` セクションは、指定された集計キーの値が変化した「後」に実行されます。また、`FINAL` 以外は、より上位の集計キーでコントロール切れが発生した場合、その上位の集計キーの `CONTROL-FOOTING` が実行される前に、下位の集計キーの `CONTROL-FOOTING` が実行されます。
  • `CONTROL-HEADING` の活用: `CONTROL-FOOTING` と同様に、`CONTROL-HEADING` を使用して、各集計レベルのヘッダー(例: 部門名、課名)を出力することも可能です。
  • `SUM` 句との連携: Report Writerの `SUM` 句と `CONTROLS` 句を組み合わせることで、より高度な集計報告書を簡単に作成できます。`SUM` 句は、指定したデータ項目を自動的に集計し、指定した集計キーでのコントロール切れ時にその合計値を参照できるようにします。
  • `USE AFTER REPORTING`: 報告書処理全体が終了した後に実行したい処理がある場合は、`USE AFTER REPORTING` 手続きを使用します。これは `CONTROL-FOOTING FINAL` と似ていますが、より汎用的な終了処理を記述できます。
  • エラーハンドリング: `CONTROLS` 句自体はエラーを直接検知するわけではありませんが、集計キーの値が予期せぬ値であったり、ソートされていないデータに対して `CONTROLS` 句を使用すると、意図しない集計結果になる可能性があります。そのため、入力データの事前チェックや、適切なソート処理が重要になります。

`CONTROLS` 句を理解し、適切に利用することで、COBOLでの報告書作成が格段に効率化されます。ぜひ、日々の開発業務で活用してみてください。

コメント

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