【実務・中級編】KEYED属性を用いた直接アクセスファイルの制御 – PL/Iの基本構文とデータ制御実践ガイド

PL/Iでキー付きファイル(VSAM)を自在に操る!KEYED属性とKEY/KEYTOオプション徹底解説

おい、諸君。最近、PL/Iで書かれた古いバッチプログラムの改修や、VSAMファイル周りの挙動に頭を悩ませているって話をよく聞くんだ。特に、キー付きファイルへのアクセスとなると、「あれ?このKEYED属性って何だっけ?」とか、「READ文でKEYオプションを使うとき、どういう挙動になるんだっけ?」なんて疑問が湧いてくること、あるだろ?

今日は、そんな諸君の悩みを一気に解消すべく、PL/Iにおける`KEYED`属性と、`READ`/`WRITE`文で使う`KEY`/`KEYTO`オプションについて、現場で培った経験を交えながら、とことん実践的に解説していく。退屈なマニュアルの丸写しなんてことはしない。現場で本当に役立つ、生きた知識を伝授するから、しっかりついてきてくれよな。

PL/Iプログラムの基本構造とファイル入出力の心構え

まず、PL/Iプログラムの基本的な構造に軽く触れておこう。PL/Iのプログラムは、通常`PACKAGE`や`PROCEDURE`で構成され、実行されるエントリーポイントには`OPTIONS(MAIN)`が付与される。

/ メインプロシージャ /
MYPROG: PROCEDURE OPTIONS(MAIN);

/ 宣言部 /
DECLARE … ;

/ 実行部 /

RETURN;
END MYPROG;

ファイル入出力、特にVSAMのようなキー付きファイルとなると、単にデータを読み書きするだけでなく、レコードの特定やエラーハンドリングが非常に重要になる。ここで、PL/Iの`ON`ユニットの存在を忘れてはならない。ファイル入出力エラーが発生した場合に、プログラムが異常終了するのを防ぎ、適切な後処理を行うための強力な味方だ。

ON ENDFILE(myfile) BEGIN;
/ ファイル終端時の処理 /
PUT SKIP LIST(‘ファイル終端に到達しました。’);
GO TO end_of_program;
END;

ON ERROR BEGIN;
/ その他のI/Oエラー時の処理 /
PUT SKIP LIST(‘I/Oエラーが発生しました。’);
/ エラーコードなどを調査し、適切な処置を行う /
GO TO abnormal_termination;
END;

これらの`ON`ユニットを適切に設定しておくことで、予期せぬエラーが発生しても、プログラムは冷静に、そして確実に処理を継続できるようになる。これが、大規模バッチ改修で培われる「堅牢なプログラム」を作るための、最初のステップであり、最も重要な心構えなんだ。

KEYED属性:キー付きファイルへの扉を開ける鍵

さて、本題の`KEYED`属性だ。これは、ファイルがキーによってレコードを識別・アクセスすることを、PL/Iコンパイラに伝えるための宣言だ。VSAMのKSDS(Key Sequenced Data Set)のようなファイルに対して、この属性を付けずにアクセスしようとすると、期待通りの動作にならないどころか、コンパイルエラーや実行時エラーの原因となる。

例えば、VSAMファイル`MYVSAM.DATA`をキーでアクセスしたい場合、以下のように宣言する。

DCL MYVSAM FILE KEYED ENVIRONMENT(VSAM);

ここで、`ENVIRONMENT(VSAM)`は、このファイルがVSAMファイルであることを明示している。`KEYED`属性が付与されていることで、PL/Iは、このファイルに対してキーを使ったランダムアクセス(直接アクセス)が可能であることを認識する。

READ文とKEYオプション:狙ったレコードをピンポイントで取得!

レコードを1件ずつ順番に読んでいくシーケンシャルアクセスとは異なり、キーを指定して目的のレコードを直接取得するのがランダムアクセスだ。PL/Iでは、`READ`文に`KEY`オプションを付けることで、これを実現する。

`KEY`オプションで指定するキーの値は、ファイル定義時に指定したキーフィールドと一致している必要がある。このキーフィールドは、`RECORD`フォーマットのファイルの場合、`BASED`属性を持つ構造体や配列として宣言し、その構造体内の特定のフィールドにキーを設定するのが一般的だ。

/ ファイル定義 /
DCL MYVSAM FILE KEYED ENVIRONMENT(VSAM);

/ レコード構造体定義 (KEY FIELDの長さはVSAM定義と一致させる) /
DCL 1 VSAM_RECORD BASED(VSAM_PTR),
2 CUST_ID CHAR(10), / キーフィールド /
2 CUST_NAME CHAR(50),
2 CUST_ADDR CHAR(100);

/ キー変数の宣言 /
DCL RECORD_KEY CHAR(10);

/ キーに設定する値 /
RECORD_KEY = ‘1234567890’;

/ KEYEDファイルからのレコード読み込み /
ON ENDFILE(MYVSAM) GO TO EOF_HANDLER; / ファイル終端時の処理 /
ON ERROR(MYVSAM) GO TO ERROR_HANDLER; / I/Oエラー時の処理 /

/ KEYオプションでレコードを特定 /
READ FILE(MYVSAM) KEY(RECORD_KEY) INTO(VSAM_RECORD);

/ 取得したレコードの処理 /
PUT SKIP LIST(‘顧客ID: ‘ || CUST_ID || ‘, 名前: ‘ || CUST_NAME);

EOF_HANDLER:
/ ファイル終端時の処理 /
RETURN;

ERROR_HANDLER:
/ I/Oエラー時の処理 /
PUT SKIP LIST(‘VSAMファイルアクセスエラーが発生しました。’);
RETURN;

この例では、`RECORD_KEY`に指定した値 `’1234567890’` に一致するキーを持つレコードが、`MYVSAM`ファイルから読み込まれる。`INTO(VSAM_RECORD)`で、読み込まれたレコードデータが`VSAM_RECORD`構造体に格納される。`VSAM_RECORD`は`BASED(VSAM_PTR)`で宣言されているが、`READ`文で`INTO`句を指定した場合、`VSAM_PTR`は自動的にレコードの開始アドレスを指すようになっている。

「キーが見つからない」エラーとの付き合い方

ここで、よくある落とし穴に触れておこう。`KEY`オプションで指定したキーを持つレコードがファイルに存在しない場合、`KEYNOTFOUND`という例外条件が発生する。これを適切に処理しないと、プログラムは異常終了してしまう。

/ … (前略) … /

/ KEYNOTFOUND例外を捕捉 /
ON KEYNOTFOUND(MYVSAM) BEGIN;
PUT SKIP LIST(‘指定されたキー: ‘ || RECORD_KEY || ‘ は見つかりませんでした。’);
/ ここで、キーが見つからなかった場合の代替処理を記述 /
/ 例えば、次のキーを試す、エラーログを出力するなど /
GO TO next_record; / 例: 次のレコード処理へ /
END;

/ KEYEDファイルからのレコード読み込み /
READ FILE(MYVSAM) KEY(RECORD_KEY) INTO(VSAM_RECORD);

/ … (後略) … /

`ON KEYNOTFOUND(ファイル名)` を使用することで、キーが見つからなかった場合の処理を記述できる。ここでは、単純にメッセージを出力し、`next_record`ラベルに分岐しているが、実際の現場では、より詳細なエラーログの記録や、代替キーでの再試行など、状況に応じた柔軟な対応が求められる。

WRITE文とKEYオプション:意図した場所にレコードを書き込む!

`WRITE`文でも`KEY`オプションは使用できる。これは、キーを指定して、そのキーに対応するレコードの場所に新しいレコードを書き込む、あるいは既存のレコードを更新する場合に用いられる。

/ … (前略) … /

/ 新規レコードの作成 /
RECORD_KEY = ‘9876543210’;
CUST_ID = ‘9876543210’;
CUST_NAME = ‘株式会社テスティング’;
CUST_ADDR = ‘東京都サンプル町’;

/ KEYオプションで書き込み先を指定 /
WRITE FILE(MYVSAM) KEY(RECORD_KEY) FROM(VSAM_RECORD);

/ … (後略) … /

この場合、`RECORD_KEY`で指定されたキーが存在しない場合は、新規レコードとして追加される。もし、そのキーを持つレコードが既に存在する場合、`WRITE`文の動作はVSAMの定義や`FILE`ステートメントのオプションによって変わるが、一般的にはレコードの更新が行われる(ただし、レコード長が同じ場合など、条件がある)。

UPDATEステートメントとの使い分け

VSAMファイルで既存レコードの更新を行う場合、`UPDATE`ステートメントを使うこともできる。`UPDATE`は、`READ`文でレコードを読み込んだ後、そのレコードを修正し、同じキーで再度書き戻す、という一連の操作を簡潔に記述できる。

/ … (前略) … /

/ 既存レコードの読み込み /
RECORD_KEY = ‘1122334455’;
READ FILE(MYVSAM) KEY(RECORD_KEY) INTO(VSAM_RECORD);

/ レコード内容の更新 /
CUST_NAME = ‘株式会社アップデート’;

/ UPDATEステートメントで更新 /
UPDATE FILE(MYVSAM) KEY(RECORD_KEY); / 読み込んだレコードを、同じキーで更新 /

/ … (後略) … /

`UPDATE`ステートメントは、`READ`で取得したレコードを直接更新するという意図が明確になるため、コードの可読性が向上する。どちらを使うべきかは、プログラムのロジックや、開発チームのコーディング規約によって判断すると良いだろう。

KEYTOオプション:レコードからキーを自動抽出!

`KEYTO`オプションは、`READ`文でレコードを読み込んだ際に、そのレコードのキーフィールドの値を、指定した変数に自動的に格納してくれる便利な機能だ。これにより、レコードからキーを別途抽出する手間が省ける。

/ … (前略) … /

/ キーを格納する変数の宣言 /
DCL READ_KEY_VALUE CHAR(10);

/ KEYEDファイルからのレコード読み込み、KEYTOオプションでキーを自動取得 /
READ FILE(MYVSAM) KEYTO(READ_KEY_VALUE) INTO(VSAM_RECORD);

/ 取得したレコードの処理 /
PUT SKIP LIST(‘読み込んだレコードのキー: ‘ || READ_KEY_VALUE || ‘, 顧客名: ‘ || CUST_NAME);

/ … (後略) … /

この例では、`MYVSAM`ファイルからレコードを読み込む際に、そのレコードのキーフィールドの値が`READ_KEY_VALUE`変数に自動的にコピーされる。これは、特にファイル全体をシーケンシャルに読みながら、キーで特定の処理を行いたい場合などに非常に役立つ。

BUILTIN関数との連携:より洗練されたファイル制御

PL/Iには、ファイル制御をより高度に行うための様々な`BUILTIN`関数が用意されている。`KEY`や`KEYTO`オプションと組み合わせることで、さらに柔軟な処理が可能になる。

例えば、`GETENV`関数を使って環境変数からキーの値を取得したり、`SUBSTR`関数でキーの一部を操作したりといったことが考えられる。

DCL MYVSAM FILE KEYED ENVIRONMENT(VSAM);
DCL 1 VSAM_RECORD BASED(VSAM_PTR),
2 ITEM_CODE CHAR(8), / キーフィールド /
2 ITEM_NAME CHAR(30);
DCL TARGET_KEY CHAR(8);
DCL ENV_VAR_KEY CHAR(100); / 環境変数から取得したキー /

/ 環境変数からキーを取得 /
ENV_VAR_KEY = GETENV(‘TARGET_ITEM_KEY’); / 環境変数名 ‘TARGET_ITEM_KEY’ /

/ 環境変数から取得した値の一部をキーとして使用 /
TARGET_KEY = SUBSTR(ENV_VAR_KEY, 1, 8); / 先頭8文字をキーとする /

ON ENDFILE(MYVSAM) GO TO EOF_HANDLER;
ON KEYNOTFOUND(MYVSAM) GO TO NOTFOUND_HANDLER;

/ KEYEDファイルからレコードを読み込み、キーを指定 /
READ FILE(MYVSAM) KEY(TARGET_KEY) INTO(VSAM_RECORD);

PUT SKIP LIST(‘取得アイテムコード: ‘ || ITEM_CODE || ‘, アイテム名: ‘ || ITEM_NAME);

EOF_HANDLER:
RETURN;

NOTFOUND_HANDLER:
PUT SKIP LIST(‘指定されたアイテムコード: ‘ || TARGET_KEY || ‘ は見つかりませんでした。’);
RETURN;

このように、`BUILTIN`関数を駆使することで、外部からの入力や環境設定に応じて、動的にキーを生成・操作することが可能になる。これは、バッチ処理でパラメータを柔軟に受け取ったり、ジョブ制御言語(JCL)で渡された情報を活用したりする際に、非常に強力な武器となる。

まとめ:現場で活きるPL/Iファイル制御の奥義

さて、今日はPL/Iにおける`KEYED`属性、`READ`/`WRITE`文の`KEY`/`KEYTO`オプション、そして`ON`ユニットや`BUILTIN`関数との連携について、実践的な解説を行ってきた。

  • `KEYED`属性: VSAMのようなキー付きファイルへのアクセスを宣言する。これがないと始まらない。
  • `READ`文 + `KEY`オプション: 指定したキーを持つレコードを直接読み込む。キーが見つからない時の`ON KEYNOTFOUND`処理は必須だ。
  • `WRITE`文 + `KEY`オプション: 指定したキーの場所にレコードを書き込む(新規または更新)。
  • `UPDATE`ステートメント: 読み込んだレコードを直接更新する際に便利。
  • `READ`文 + `KEYTO`オプション: 読み込んだレコードのキーを自動的に変数に格納する。
  • `ON`ユニット: `ENDFILE`, `ERROR`, `KEYNOTFOUND`などの例外条件を捕捉し、プログラムの堅牢性を高める。
  • `BUILTIN`関数: 環境変数取得(`GETENV`)や文字列操作(`SUBSTR`)などと連携させ、より動的で柔軟なファイル制御を実現する。

これらの知識は、単にPL/Iの言語仕様を理解するだけでなく、実際のメインフレームシステムがどのように動作しているのか、そして、いざという時にどうやってトラブルシューティングを行うのか、という現場のエンジニアに不可欠な「勘」や「センス」を磨くための土台となる。

今回の解説が、君たちのPL/Iプログラミング、そしてメインフレームシステム保守・開発の一助となれば幸いだ。これからも、現場で培った知見を、こうして惜しみなく伝えていくから、期待していてくれよ。何か分からないことがあれば、いつでも声をかけてくれ。

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