こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語の経験がある方にとって、歴史あるIBMメインフレームの世界、そしてPL/I(ピーエルワン)という言語は、どこか近寄りがたい「要塞」のように見えるかもしれません。
特に、「データ定義のクセが強い」「なんだか記号だらけで難しそう」といった不安を抱えていませんか? でも、安心してくださいね。基本のルールさえ掴んでしまえば、PL/Iは驚くほど合理的で、プログラマの意図に素直に従ってくれる優しい言語なんです。
今回は、そんなPL/Iの世界で避けて通れない「DB2埋め込みSQLにおける動的SQL(PREPARE / EXECUTE)」をテーマに、JavaやCOBOLの感覚とはちょっと違うPL/Iならではの面白さとコツを、一緒に紐解いていきましょう!
—
そもそもPL/Iの識別子って、何でもアリって本当?
JavaやCOBOLを触ってきた方なら、「予約語(IFやMOVEなど)と同じ名前をデータ項目名(変数名)にしてはいけない」という鉄則をご存知のはずです。もしやったら、コンパイラに激しく怒られてしまいますよね。
ところが、PL/Iの懐の深さは(あるいは自由奔放さは)桁違いです。なんと、PL/Iには「真の意味での予約語」が存在しません。
どういうことかと言うと、極端な話、`IF` という名前の変数を宣言して、その変数を条件分岐に使うことさえ、文脈によってはコンパイラが「あ、ここは変数ね」「ここはキーワードね」と空気َحを読んで解釈してくれます。
「じゃあ、どれだけ適当に書いてもいいんだ!」と思われるかもしれませんが、実務の現場では、後で見返す人が発狂しないよう、常識的な名前をつけるのが大人のマナーです(笑)。
そんな柔軟なPL/Iを使って、今回は「実行するまでどんなSQL文が飛んでくるか分からない!」というスリリングな状況、動動的SQLを攻略してみましょう。
—
動的SQLの基本:なぜ `PREPARE` と `EXECUTE` が必要なのか?
通常、バッチプログラムの中で使うSQL(静的SQL)は、ソースコードを書いた時点で「このテーブルからこの列を取る」とガチガチに固定されています。コンパイル時にDB2が実行計画を最適化してくれるので安心ですね。
しかし、「画面や外部ファイルから条件を受け取って、WHERE句の条件が毎回ガラリと変わる」「集計対象のテーブル名すら動的に変えたい」という要件はどうでしょう?
ここで登場するのが 動的SQL です。
手順は大きく分けて以下の3ステップです。イメージとしては、「文字で作ったSQLの設計図を、DB2に下見(PREPARE)させて、その場で実行(EXECUTE)する」という流れになります。
1. SQL文を格納する文字変数(ホスト変数)を用意する。
2. その文字列を `PREPARE` 文でDB2にコンパイルしてもらい、「実行の準備(ステートメント・ネームの付与)」を行う。
3. 準備されたステートメントを `EXECUTE` 文で発射する!
では、実際のPL/Iコードを見てみましょう。「うわっ、大文字ばかりで目が回りそう…」と思うかもしれませんが、一つずつ日本語のコメントを追いかけながら見ていけば大丈夫ですよ。
—
実践!PL/Iによる動的SQL構築コード例
以下のコードは、入力された条件(例えば顧客ランク)に応じて、動的にWHERE句を変更しながら顧客テーブルを更新するバッチプログラムのイメージです。
1
—————————————————————-
- 動的SQL(PREPARE / EXECUTE)のサンプルプログラム
—————————————————————-
DYNAMIC_SQL_SAMPLE: PROC OPTIONS(MAIN);
— 1. DB2通信用のSQLCA(SQL通信エリア)の定義 —
EXEC SQL INCLUDE SQLCA;
— 2. 変数の宣言(PL/I独特のデータ属性に注目!) —
DCL WK_SQL_STRING CHAR(500) VAR; — 動的SQL格納用(可変長文字列) —
DCL WK_CUST_RANK CHAR(1); — 検索条件の顧客ランク —
DCL WK_NEW_DISC DEC FIXED(5,2); — 更新する割引率 —
— 3. 入力値の取得(ここでは仮に代入) —
WK_CUST_RANK = ‘A’;
WK_NEW_DISC = 15.50;
————————————————————
- 4. 動的SQL文の組み立て(文字列結合演算子 ‘||’ を使用)
————————————————————
WK_SQL_STRING = ‘UPDATE CUSTOMER_TBL ‘
|| ‘SET DISCOUNT_RATE = :H_DISC ‘
|| ‘WHERE CUSTOMER_RANK = ”’ || WK_CUST_RANK || ””;
— 生成されたSQL文字列を確認(ログ出力などのイメージ) —
PUT SKIP EDIT (‘BUILD SQL: ‘, WK_SQL_STRING) (A, A);
————————————————————
- 5. PREPARE文:DB2にSQL文をコンパイルさせる
- ※ ‘STMT1’ はこのプログラム内で通用するあだ名(識別子)
————————————————————
EXEC SQL PREPARE STMT1 FROM :WK_SQL_STRING;
IF SQLCODE ^= 0 THEN DO;
PUT SKIP EDIT (‘PREPARE ERROR: ‘, SQLCODE) (A, Z);
SIGNAL ERROR;
END;
————————————————————
- 6. EXECUTE文:準備されたステートメントを実行する
- ※ パラメータマーカー(:H_DISC)に変数をバインド
————————————————————
EXEC SQL EXECUTE STMT1 USING :WK_NEW_DISC;
IF SQLCODE ^= 0 THEN DO;
PUT SKIP EDIT (‘EXECUTE ERROR: ‘, SQLCODE) (A, Z);
SIGNAL ERROR;
END;
ELSE DO;
PUT SKIP EDIT (‘UPDATE SUCCESS!’) (A);
EXEC SQL COMMIT;
END;
END DYNAMIC_SQL_SAMPLE;
—
ここがポイント!PL/IとDB2連携の急所
上記のコードで、JavaやCOBOL出身者が思わず「おっ?」と躓きやすいポイントを優しく解説しますね。
① `CHAR(500) VAR` ってなに?(可変長文字列)
PL/Iで文字を扱うとき、`CHAR(500)` と書くと「常に500バイトの固定長」になります。しかし、動的SQL文は作るたびに長さが変わりますよね。そこで後ろに `VAR`(Varyingの略) をつけることで、「今の実際の文字の長さに合わせてくれる可変長文字列」になります。COBOLの `PIC X(500) VARYING` のようなものですね。これによって、SQL文を `||` で安全に連結できるようになります。
② パラメータマーカー(`?` または `:ホスト変数`)の使い分け
SQL文の中で値が変動する部分(上記の例では割引率の値など)には、直接リテラルを埋め込むのではなく、プレースホルダー(パラメータマーカー)を使うのがセキュアかつ高速(キャッシュ効力あり)です。
PL/Iの埋め込みSQLでは、ホスト変数をコロン `:` 付きでそのまま動的SQLの文字列に組み込むか、あるいはSQL文側を `?` にしておいて `EXECUTE … USING …` で渡す手法が使えます。状況に応じて使い分けましょう。
③ 不思議な演算子 `^=`
コードの中に `SQLCODE ^= 0` という見慣れない記号が出てきました。
これはJavaの `!=` や、数学の `≠`(等しくない)を意味します。PL/Iでは `^`(サーカムフレックス、ハットマーク)が否定(NOT)を表すため、`^=` で「ノット・イコール」になります。最初はギョッとするかもしれませんが、慣れるとスタイリッシュに見えてくるから不思議です。
—
まとめ
今回は、PL/Iにおける識別子の懐の深さと、実務で必須となる動的SQLの `PREPARE` / `EXECUTE` についてご紹介しました。
レガシーな世界や言語仕様は、最初は独特の記号やルールに圧倒されてしまうかもしれません。でも、一つひとつの構文が「なぜその形をしているのか」という背景を覗いてみると、非常に合理的でプログラマブルに作られていることが分かります。
「怖いもの知らず」で飛び込む必要はありません。分からないことがあれば、またこうして一つずつ、優しく紐解いていけば大丈夫です。
あなたのメインフレームライフ、そしてPL/Iのコーディングが少しでも楽しいものになりますように!
