COBOLのOCCURS句は、同じ構造を持つデータを繰り返し定義するための機能です。他の言語の「配列」に相当し、月別売上・商品マスタ・コード変換テーブルなど、業務プログラムのあらゆる場面で活用されます。
この記事では、OCCURS句の基本定義から、INDEXED BY句・SEARCH/SEARCH ALL文・DEPENDING ON句(可変長配列)・多次元配列まで、実務で必要な知識を体系的に解説します。ループ処理(PERFORM VARYING)の詳細はPERFORM文完全ガイドで扱っているため、本記事はDATA DIVISION側の定義と配列検索に焦点を当てます。
- OCCURS句の基本構文と配置ルール・制約
- 数値・文字・グループ項目の配列定義パターン
- 多次元配列(2次元・3次元)の定義
- DEPENDING ON句による可変長配列
- INDEXED BY句の役割とSEARCH文(順次検索)
- KEY IS句とSEARCH ALL文(二分探索)
- コード変換テーブル・商品マスタの実践パターン
OCCURS句とは
OCCURS句は、同じ構造を持つデータ項目を指定した個数だけ連続して定義するための記述です。定義した各要素は「添字(インデックス)」で識別します。
| 種類 | 構文 | 用途 |
|---|---|---|
| 静的配列 | OCCURS n TIMES | 要素数が固定の配列 |
| 可変長配列 | OCCURS m TO n TIMES DEPENDING ON 変数 | 要素数を実行時に変動させる配列 |
| インデックス付き配列 | OCCURS n TIMES INDEXED BY インデックス名 | SEARCH文で検索可能な配列 |
| 整列テーブル | OCCURS n TIMES ASCENDING/DESCENDING KEY IS キー項目 INDEXED BY インデックス名 | SEARCH ALL(二分探索)が使えるテーブル |
基本構文と配置ルール
レベル番号 データ名 OCCURS 要素数 TIMES
[ASCENDING/DESCENDING KEY IS キー項目 ...]
[INDEXED BY インデックス名 ...]
PIC 句 または 従属項目の定義.
OCCURS句の記述位置についてのルールを押さえておきましょう。
| レベル番号 | 使用可否 | 備考 |
|---|---|---|
| 01・77レベル | 使用不可 | — |
| 02〜49レベル | 使用可 | 通常の配列定義 |
| 66レベル(RENAMES) | 使用不可 | — |
| 88レベル(条件名) | 使用不可 | — |
01・77レベルにはOCCURS句を書けない
配列を定義したい場合は、01レベルのグループ項目の下に02〜49レベルでOCCURS句を書きます。
* NG: 01レベルにOCCURSは書けない
01 WS-SCORE PIC 9(3) OCCURS 10 TIMES. *> コンパイルエラー
* OK: グループ項目の下に定義する
01 WS-SCORE-TABLE.
05 WS-SCORE PIC 9(3) OCCURS 10 TIMES.
1次元配列の定義パターン
数値配列
WORKING-STORAGE SECTION.
01 WS-MONTHLY-SALES-TBL.
05 WS-MONTHLY-SALES PIC 9(9)V99 OCCURS 12 TIMES.
PROCEDURE DIVISION.
*> 特定月の売上を参照(添字は1始まり)
DISPLAY '1月: ' WS-MONTHLY-SALES(1)
DISPLAY '3月: ' WS-MONTHLY-SALES(3)
*> 合計を求めるループ
MOVE 0 TO WS-TOTAL
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 12
ADD WS-MONTHLY-SALES(WS-IDX) TO WS-TOTAL
END-PERFORM.
文字配列
WORKING-STORAGE SECTION.
01 WS-ERR-MSG-TBL.
05 WS-ERR-MSG PIC X(80) OCCURS 5 TIMES.
PROCEDURE DIVISION.
*> メッセージを設定
MOVE '入力値が空です' TO WS-ERR-MSG(1)
MOVE '日付の形式が正しくありません' TO WS-ERR-MSG(2)
MOVE '金額が0以下です' TO WS-ERR-MSG(3)
*> エラーコードに対応するメッセージを表示
DISPLAY WS-ERR-MSG(WS-ERR-CODE).
グループ項目の配列(構造体の配列)
OCCURS句はグループ項目(複数フィールドを持つ複合型)にも適用できます。他の言語の「構造体の配列」に相当し、商品マスタや社員テーブルの定義に使います。
WORKING-STORAGE SECTION.
01 WS-ITEM-TABLE.
05 WS-ITEM-ENTRY OCCURS 100 TIMES.
10 WS-ITEM-CODE PIC X(6). *> 商品コード
10 WS-ITEM-NAME PIC X(20). *> 商品名
10 WS-ITEM-PRICE PIC 9(7)V99. *> 単価
10 WS-ITEM-STOCK PIC 9(5). *> 在庫数
01 WS-ITEM-COUNT PIC 9(3) VALUE 0. *> 有効件数
01 WS-IDX PIC 9(3).
PROCEDURE DIVISION.
*> 商品データをセット
MOVE 'ITM001' TO WS-ITEM-CODE(1)
MOVE 'ノートPC' TO WS-ITEM-NAME(1)
MOVE 98000.00 TO WS-ITEM-PRICE(1)
MOVE 50 TO WS-ITEM-STOCK(1)
MOVE 1 TO WS-ITEM-COUNT
*> 特定商品の在庫を更新
SUBTRACT 1 FROM WS-ITEM-STOCK(1)
*> 全商品一覧を表示
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > WS-ITEM-COUNT
DISPLAY WS-ITEM-CODE(WS-IDX) ' '
WS-ITEM-NAME(WS-IDX) ' '
WS-ITEM-PRICE(WS-IDX) '円 '
WS-ITEM-STOCK(WS-IDX) '個'
END-PERFORM.
多次元配列(2次元・3次元)
OCCURS句をネストすることで2次元・3次元の配列を定義できます。帳票の行列・年月日の組み合わせデータ・座席管理などに使います。
2次元配列
WORKING-STORAGE SECTION.
01 WS-DEPT-SALES-TBL.
05 WS-DEPT-ROW OCCURS 5 TIMES. *> 5部門
10 WS-MONTH-SALES PIC 9(9)V99
OCCURS 12 TIMES. *> 12か月
01 WS-DEPT-IDX PIC 9(2).
01 WS-MONTH-IDX PIC 9(2).
01 WS-TOTAL PIC 9(12)V99 VALUE 0.
PROCEDURE DIVISION.
*> 2次元配列への参照: (部門インデックス, 月インデックス)
MOVE 1500000.00 TO WS-MONTH-SALES(1, 3) *> 1部門3月に代入
*> 全体合計を求める2重ループ
PERFORM VARYING WS-DEPT-IDX FROM 1 BY 1
UNTIL WS-DEPT-IDX > 5
PERFORM VARYING WS-MONTH-IDX FROM 1 BY 1
UNTIL WS-MONTH-IDX > 12
ADD WS-MONTH-SALES(WS-DEPT-IDX, WS-MONTH-IDX)
TO WS-TOTAL
END-PERFORM
END-PERFORM
DISPLAY '年間総売上: ' WS-TOTAL.
3次元配列
WORKING-STORAGE SECTION.
01 WS-ANNUAL-TBL.
05 WS-YEAR-ROW OCCURS 3 TIMES. *> 3年分
10 WS-DEPT-ROW OCCURS 5 TIMES. *> 5部門
15 WS-MON-AMT PIC 9(9)V99
OCCURS 12 TIMES.*> 12か月
PROCEDURE DIVISION.
*> 3次元参照: (年, 部門, 月)
MOVE 2000000.00 TO WS-MON-AMT(1, 2, 6) *> 1年目・2部門・6月
*> PERFORM VARYING...AFTERで多重ループも可能(詳細はPERFOM文記事参照)
PERFORM VARYING WS-YEAR-IDX FROM 1 BY 1 UNTIL WS-YEAR-IDX > 3
AFTER WS-DEPT-IDX FROM 1 BY 1 UNTIL WS-DEPT-IDX > 5
AFTER WS-MONTH-IDX FROM 1 BY 1 UNTIL WS-MONTH-IDX > 12
ADD WS-MON-AMT(WS-YEAR-IDX, WS-DEPT-IDX, WS-MONTH-IDX)
TO WS-GRAND-TOTAL
END-PERFORM.
COBOLの添字はカンマ区切り(または空白区切り)で指定します。WS-MON-AMT(1, 2, 6) と WS-MON-AMT(1 2 6) は同じ意味です。多次元ループの詳細はPERFORM文完全ガイド(VARYING…AFTER)を参照してください。
DEPENDING ON句(可変長配列)
OCCURS … DEPENDING ON句を使うと、要素数を実行時に変動させることができます。ファイルから読み込んだ件数に合わせて配列サイズを決めるパターンなどで使います。
01 WS-DATA-TABLE.
05 WS-DATA-COUNT PIC 9(3). *> 現在の要素数
05 WS-DATA-ENTRY OCCURS 1 TO 100 TIMES
DEPENDING ON WS-DATA-COUNT. *> 1〜100の可変
10 WS-DATA-KEY PIC X(6).
10 WS-DATA-VAL PIC 9(7)V99.
WORKING-STORAGE SECTION.
01 WS-LOAD-TBL.
05 WS-LOAD-COUNT PIC 9(3) VALUE 0.
05 WS-LOAD-ENTRY OCCURS 1 TO 200 TIMES
DEPENDING ON WS-LOAD-COUNT.
10 WS-PROD-CD PIC X(6).
10 WS-PROD-RATE PIC 9(3)V99.
PROCEDURE DIVISION.
OPEN INPUT RATE-FILE
PERFORM UNTIL WS-EOF OR WS-LOAD-COUNT >= 200
READ RATE-FILE
AT END MOVE 'Y' TO WS-EOF-FLAG
NOT AT END
ADD 1 TO WS-LOAD-COUNT
MOVE RF-PROD-CD TO WS-PROD-CD(WS-LOAD-COUNT)
MOVE RF-PROD-RATE TO WS-PROD-RATE(WS-LOAD-COUNT)
END-READ
END-PERFORM
CLOSE RATE-FILE
DISPLAY WS-LOAD-COUNT '件をロードしました'.
DEPENDING ON変数の管理は厳密に
DEPENDING ON変数(現在の要素数)が実際のデータ件数と一致していないと、添字範囲外アクセスが発生します。要素を追加・削除するときは必ずDEPENDING ON変数も更新してください。また最大要素数(n)を超えた添字アクセスは実行時エラーです。
INDEXED BY句とSEARCH文(順次検索)
INDEXED BY句を付けると、配列に「インデックス型」の変数が紐付きます。インデックス型はPIC 9変数と異なり、バイトオフセットで内部管理されるため算術演算には使えませんが、SEARCH文(順次検索)を使うための必須条件です。
| 操作 | 構文 | 備考 |
|---|---|---|
| 宣言 | INDEXED BY インデックス名 | データ定義で宣言 |
| 初期化 | SET インデックス名 TO 1 | SETで1から始める(MOVE不可) |
| 増減 | SET インデックス名 UP BY 1 / DOWN BY 1 | SETで増減(算術演算不可) |
| PIC 9変数との変換 | SET 整数変数 TO インデックス名 | 相互変換にもSETを使う |
WORKING-STORAGE SECTION.
01 WS-CODE-TABLE.
05 WS-CODE-ENTRY OCCURS 50 TIMES INDEXED BY WS-CODE-IDX.
10 WS-CODE PIC X(4).
10 WS-CODE-NAME PIC X(20).
01 WS-WORK-IDX PIC 9(2). *> 通常変数(添字として使用可)
PROCEDURE DIVISION.
*> OK: SET文でインデックスを操作する
SET WS-CODE-IDX TO 1 *> 先頭に移動
SET WS-CODE-IDX UP BY 1 *> 次の要素へ
SET WS-CODE-IDX DOWN BY 3 *> 3つ前へ
*> インデックスと整数変数の相互変換
SET WS-WORK-IDX TO WS-CODE-IDX *> インデックス→整数
SET WS-CODE-IDX TO WS-WORK-IDX *> 整数→インデックス
*> NG: 算術演算や直接MOVEはエラー
* ADD 1 TO WS-CODE-IDX *> エラー
* MOVE 5 TO WS-CODE-IDX *> エラー
SEARCH文(AT END・WHEN)
SEARCH文はOCCURS … INDEXED BYで定義した配列を順次検索します。テーブルの先頭から1要素ずつ条件を評価し、一致したところで処理を実行します。
SET インデックス名 TO 1
SEARCH テーブル名
AT END
*> 見つからなかったときの処理
WHEN 条件式
*> 条件に一致したときの処理
END-SEARCH
WORKING-STORAGE SECTION.
01 WS-DEPT-TABLE.
05 WS-DEPT-ENTRY OCCURS 20 TIMES INDEXED BY WS-DEPT-IDX.
10 WS-DEPT-CODE PIC X(4).
10 WS-DEPT-NAME PIC X(20).
01 WS-SEARCH-CODE PIC X(4).
01 WS-FOUND-NAME PIC X(20) VALUE SPACES.
01 WS-FOUND-FLG PIC X(1) VALUE 'N'.
PROCEDURE DIVISION.
*> テーブルにデータをロード(省略)
MOVE 'D001' TO WS-DEPT-CODE(1)
MOVE '営業部' TO WS-DEPT-NAME(1)
MOVE 'D002' TO WS-DEPT-CODE(2)
MOVE '開発部' TO WS-DEPT-NAME(2)
*> ...(以下略)
MOVE 'D002' TO WS-SEARCH-CODE
*> SEARCH前に必ずSET TO 1でインデックスを初期化
SET WS-DEPT-IDX TO 1
SEARCH WS-DEPT-ENTRY
AT END
DISPLAY '該当する部門コードがありません: ' WS-SEARCH-CODE
MOVE 'N' TO WS-FOUND-FLG
WHEN WS-DEPT-CODE(WS-DEPT-IDX) = WS-SEARCH-CODE
MOVE WS-DEPT-NAME(WS-DEPT-IDX) TO WS-FOUND-NAME
MOVE 'Y' TO WS-FOUND-FLG
END-SEARCH
IF WS-FOUND-FLG = 'Y'
DISPLAY '部門名: ' WS-FOUND-NAME
END-IF.
SEARCH前に必ずSET TO 1
SEARCH文はインデックスの現在位置から検索を開始します。SET WS-IDX TO 1を書き忘れると、前回の検索で進んだインデックス位置から続きを検索してしまい、先頭のデータが見つからないバグが発生します。
KEY IS句とSEARCH ALL(二分探索)
OCCURS句にASCENDING KEY IS(または DESCENDING KEY IS)とINDEXED BYを組み合わせると、SEARCH ALL文(二分探索)が使えるようになります。大量のデータを高速検索する場合に有効で、線形検索(SEARCH)より大幅に高速です。
SEARCH vs SEARCH ALL の使い分け
- SEARCH(順次検索): ソートされていないテーブルにも使える。要素数が少ない(〜100件程度)場合に向いている
- SEARCH ALL(二分探索): テーブルが昇順または降順にソート済みであることが前提。数百〜数万件の大規模テーブルに向いている
WORKING-STORAGE SECTION.
01 WS-PRODUCT-TABLE.
05 WS-PROD-ENTRY OCCURS 1000 TIMES
ASCENDING KEY IS WS-PROD-CODE
INDEXED BY WS-PROD-IDX.
10 WS-PROD-CODE PIC X(6).
10 WS-PROD-NAME PIC X(30).
10 WS-PROD-PRICE PIC 9(7)V99.
10 WS-PROD-STOCK PIC 9(5).
*> SEARCH ALL は AT END と WHEN のみ使用可(SET TO 1は不要)
WORKING-STORAGE SECTION.
01 WS-SEARCH-CODE PIC X(6).
01 WS-RESULT-PRICE PIC 9(7)V99.
01 WS-FOUND-FLG PIC X VALUE 'N'.
PROCEDURE DIVISION.
MOVE 'ITM042' TO WS-SEARCH-CODE
SEARCH ALL WS-PROD-ENTRY
AT END
DISPLAY '商品コードが見つかりません: ' WS-SEARCH-CODE
WHEN WS-PROD-CODE(WS-PROD-IDX) = WS-SEARCH-CODE
MOVE WS-PROD-PRICE(WS-PROD-IDX) TO WS-RESULT-PRICE
MOVE 'Y' TO WS-FOUND-FLG
END-SEARCH
IF WS-FOUND-FLG = 'Y'
DISPLAY '単価: ' WS-RESULT-PRICE
END-IF.
| 比較項目 | SEARCH(順次) | SEARCH ALL(二分) |
|---|---|---|
| 前提条件 | 不要 | KEY IS句 + INDEXED BY + データのソート済みが必須 |
| 検索速度 | O(n)(全件走査の可能性) | O(log n)(二分探索) |
| インデックス初期化 | SET TO 1 が必須 | 不要(自動で最適位置から開始) |
| WHEN条件 | 任意の条件式 | 等値比較(=)のみ。ASCENDING/DESCENDING KEYの項目のみ |
| 複数条件 | WHEN … WHEN … と複数書ける | 1つの等値条件のみ |
配列の初期化
OCCURS項目を含むグループ全体を初期化する方法をまとめます。
WORKING-STORAGE SECTION.
01 WS-NUM-TABLE.
05 WS-NUM PIC 9(5)V99 OCCURS 100 TIMES.
01 WS-CHR-TABLE.
05 WS-CHR PIC X(10) OCCURS 50 TIMES.
01 WS-IDX PIC 9(3).
PROCEDURE DIVISION.
*> パターン1: INITIALIZE で一括初期化(数値→0、文字→スペース)
INITIALIZE WS-NUM-TABLE *> 全要素を0にする
INITIALIZE WS-CHR-TABLE *> 全要素をスペースにする
*> パターン2: MOVE ZEROS / SPACES でグループ全体をクリア
MOVE ZEROS TO WS-NUM-TABLE *> 数値テーブル全体を0クリア
MOVE SPACES TO WS-CHR-TABLE *> 文字テーブル全体をスペースクリア
*> パターン3: PERFORMでループして個別初期化
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 100
MOVE 0 TO WS-NUM(WS-IDX)
END-PERFORM
*> パターン4: VALUE句で宣言時に初期値設定(静的のみ・DEPENDING ON不可)
* 05 WS-FLAG OCCURS 10 TIMES VALUE 'N'. *> 全要素'N'で初期化
初期化の推奨順序: 配列全体をまとめてクリアするなら INITIALIZE テーブル名 または MOVE ZEROS TO テーブル名 が最も簡潔です。要素ごとに異なる初期値を設定する場合はPERFORMループを使います。
実践パターン
コード変換テーブル(都道府県コード→名称)
コードから名称に変換するテーブルは業務プログラムの定番パターンです。件数が少ない(〜50件)場合はSEARCH、大量の場合はSEARCH ALLを使います。
WORKING-STORAGE SECTION.
01 WS-PREF-TABLE.
05 WS-PREF-ENTRY OCCURS 47 TIMES
ASCENDING KEY IS WS-PREF-CODE
INDEXED BY WS-PREF-IDX.
10 WS-PREF-CODE PIC 9(2).
10 WS-PREF-NAME PIC X(8).
01 WS-SEARCH-PREF PIC 9(2).
01 WS-RESULT-NAME PIC X(8) VALUE SPACES.
PROCEDURE DIVISION.
*> テーブルをコード順(昇順)に設定する
MOVE 1 TO WS-PREF-CODE(1)
MOVE '北海道' TO WS-PREF-NAME(1)
MOVE 2 TO WS-PREF-CODE(2)
MOVE '青森県' TO WS-PREF-NAME(2)
*> ... 47都道府県分設定(省略)
*> 二分探索でコードから名称を取得
MOVE 13 TO WS-SEARCH-PREF
SEARCH ALL WS-PREF-ENTRY
AT END
MOVE '不明 ' TO WS-RESULT-NAME
WHEN WS-PREF-CODE(WS-PREF-IDX) = WS-SEARCH-PREF
MOVE WS-PREF-NAME(WS-PREF-IDX) TO WS-RESULT-NAME
END-SEARCH
DISPLAY '都道府県名: ' WS-RESULT-NAME.
累積集計テーブルへの書き込み
WORKING-STORAGE SECTION.
01 WS-DAILY-TBL.
05 WS-DAY-ROW OCCURS 31 TIMES. *> 31日分
10 WS-DEPT-AMT PIC 9(9)V99 OCCURS 5 TIMES. *> 5部門
01 WS-DAY-IDX PIC 9(2).
01 WS-DEPT-IDX PIC 9(2).
PROCEDURE DIVISION.
*> 全体を0クリア
MOVE ZEROS TO WS-DAILY-TBL
*> ファイルから読み込んだレコード(SR-DAY, SR-DEPT, SR-AMT)を集計
PERFORM UNTIL WS-EOF
ADD SR-AMT TO WS-DEPT-AMT(SR-DAY, SR-DEPT)
READ SALES-FILE
AT END MOVE 'Y' TO WS-EOF-FLAG
END-READ
END-PERFORM
*> 日別・部門別に出力
PERFORM VARYING WS-DAY-IDX FROM 1 BY 1 UNTIL WS-DAY-IDX > 31
PERFORM VARYING WS-DEPT-IDX FROM 1 BY 1 UNTIL WS-DEPT-IDX > 5
IF WS-DEPT-AMT(WS-DAY-IDX, WS-DEPT-IDX) > 0
DISPLAY WS-DAY-IDX '日 部門' WS-DEPT-IDX
': ' WS-DEPT-AMT(WS-DAY-IDX, WS-DEPT-IDX)
END-IF
END-PERFORM
END-PERFORM.
マスタテーブルの一括ロードと参照
IDENTIFICATION DIVISION.
PROGRAM-ID. TAX-CALC.
DATA DIVISION.
WORKING-STORAGE SECTION.
* 消費税率テーブル(適用開始日昇順)
01 WS-TAX-TABLE.
05 WS-TAX-ENTRY OCCURS 1 TO 20 TIMES
DEPENDING ON WS-TAX-COUNT
ASCENDING KEY IS WS-TAX-FROM
INDEXED BY WS-TAX-IDX.
10 WS-TAX-FROM PIC 9(8). *> 適用開始日 YYYYMMDD
10 WS-TAX-RATE PIC V99. *> 税率(0.08, 0.10など)
01 WS-TAX-COUNT PIC 9(2) VALUE 0.
01 WS-TODAY PIC 9(8).
01 WS-PRICE PIC 9(7)V99.
01 WS-TAX PIC 9(7)V99.
01 WS-APPLIED-RATE PIC V99 VALUE 0.
PROCEDURE DIVISION.
*> 税率テーブルの初期設定
MOVE 19970401 TO WS-TAX-FROM(1) MOVE 0.05 TO WS-TAX-RATE(1)
MOVE 20140401 TO WS-TAX-FROM(2) MOVE 0.08 TO WS-TAX-RATE(2)
MOVE 20191001 TO WS-TAX-FROM(3) MOVE 0.10 TO WS-TAX-RATE(3)
MOVE 3 TO WS-TAX-COUNT
*> 処理日の税率を逆順で探索(最新の適用開始日から)
MOVE FUNCTION CURRENT-DATE(1:8) TO WS-TODAY
PERFORM VARYING WS-TAX-IDX FROM WS-TAX-COUNT BY -1
UNTIL WS-TAX-IDX < 1
IF WS-TAX-FROM(WS-TAX-IDX) <= WS-TODAY
MOVE WS-TAX-RATE(WS-TAX-IDX) TO WS-APPLIED-RATE
EXIT PERFORM
END-IF
END-PERFORM
*> 税額計算
MOVE 10000.00 TO WS-PRICE
COMPUTE WS-TAX = WS-PRICE * WS-APPLIED-RATE
DISPLAY '税額: ' WS-TAX.
よくある落とし穴と対策
① 添字の範囲外アクセス
症状: OCCURS 10 TIMESで定義した配列に対してWS-IDX = 11などの値でアクセスすると、実行時エラーまたは別の変数領域を破壊するバグが発生します。
対策: ループの終了条件を UNTIL WS-IDX > 要素数 と正確に書き、ループ外からのアクセス前にも添字の範囲チェックを行います。DEPENDING ON変数を使う場合は、要素追加前にカウントが上限を超えていないか確認してください。
② 01レベルにOCCURSを書いてコンパイルエラー
症状: 01 WS-TABLE PIC X(10) OCCURS 5 TIMES. のように01レベルに書くとコンパイルエラーになります。初心者によく見られるミスです。
対策: 01レベルはグループ項目の親として定義し、OCCURS句は必ず02〜49レベルに記述します。
③ SEARCH前にSET TO 1を書き忘れる
症状: SEARCH文(順次検索)はインデックスの現在値から検索を開始します。前回のSEARCHでインデックスが中途半端な位置のまま次の検索に入ると、先頭付近のデータが見つからないバグが発生します。
対策: SERACHの直前に必ず SET インデックス名 TO 1 を記述して先頭から検索を開始します。SEARCH ALL(二分探索)の場合は不要です。
④ SEARCH ALLでデータがソートされていない
症状: SEARCH ALLはデータがKEY ISで指定したキーの昇順(または降順)にソートされていることを前提とします。ソートされていない状態でSEARCH ALLを実行すると、見つかるべきデータが「見つからない(AT END)」という誤った結果になります。
対策: SEARCH ALLを使うテーブルはキー順にデータをロードするか、ロード後にSORT処理をかけます。ソートが保証できない場合はSEARCH(順次検索)を使います。
⑤ インデックス型変数に算術演算を行う
症状: INDEXED BYで定義したインデックス変数にADD 1やMOVEを直接行おうとするとコンパイルエラーになります。インデックス型はバイトオフセットで管理されており、通常の整数変数と内部表現が異なります。
対策: インデックスの操作はすべて SET インデックス名 TO 値・SET インデックス名 UP BY 1・SET インデックス名 DOWN BY 1 を使います。整数変数との相互変換も SET 整数変数 TO インデックス名 で行います。
よくある質問
まとめ
| 機能 | 構文 | 特徴 |
|---|---|---|
| 静的配列 | OCCURS n TIMES | 要素数固定・通常の配列 |
| 可変長配列 | OCCURS m TO n TIMES DEPENDING ON 変数 | 実行時に要素数を変動させる |
| インデックス付き | OCCURS n TIMES INDEXED BY 名前 | SEARCH文(順次検索)が使える |
| 整列テーブル | ASCENDING/DESCENDING KEY IS キー + INDEXED BY | SEARCH ALL(二分探索)が使える |
| 2次元配列 | OCCURS句をネスト | (行, 列)の2添字でアクセス |
| 初期化 | INITIALIZE / MOVE ZEROS・SPACES | グループ全体を一括クリア |
OCCURS句はCOBOLの配列機能の核です。INDEXED BY・SEARCH・SEARCH ALLを使いこなすことで、大規模テーブルの高速検索から固定長ファイルの処理まで幅広い業務要件に対応できます。
ループ処理の詳細はPERFORM文完全ガイドを、同じメモリ領域の多重定義はREDEFINES句完全ガイドも合わせてご確認ください。