COBOLでたくさんのデータが詰まったテーブルの中から、目的のデータを探し出す時、どうしていますか?
一つ一つ順番に見ていくのも良いですが、もっと効率的な方法があるんです。それが、COBOLが用意してくれている便利な命令、「SEARCH文」なんです!
でも、「SEARCH文ってなんだか難しそう…」「SEARCHとSEARCH ALLって何が違うの?」なんて、ちょっと尻込みしちゃう気持ち、すごく分かります。
大丈夫、心配いりません!この記事を読めば、SEARCH文の「?」が「!」に変わるはずです。
さあ、一緒にSEARCH文の世界を探検してみましょう! きっとあなたのCOBOLスキルが一段階レベルアップしますよ!
この記事で学べること
- SEARCH文がどんな時に役立つか
- SEARCH文の基本的な書き方のルール
- 逐次サーチ(SEARCH)と二分探索サーチ(SEARCH ALL)の違いと使い方
- SEARCH文を使うためのテーブル定義の方法
- 実際に動かせるサンプルコードとその解説
- SEARCH文を使う時に気をつけたいポイント
COBOLの「SEARCH文」とは?テーブル検索の基本を理解しよう
まず、COBOLのSEARCH文が一体何者なのか、その役割から見ていきましょう。
簡単に言うと、SEARCH文は「テーブル(配列)の中から、指定した条件に合うデータを見つけ出すための命令」です。
例えば、社員番号が100人分入ったテーブルから、「社員番号50番の人」のデータを探したい!なんて時。
もしSEARCH文がなかったら、プログラムで1番目から100番目まで順番に、「社員番号は50番かな?」と一つずつチェックしていくコードを書く必要があります。データが少なければそれでも良いのですが、何千、何万件とデータが増えたら大変ですよね?処理に時間もかかってしまいます。
そこで登場するのがSEARCH文!
SEARCH文を使えば、COBOLが効率的な方法でテーブル内を探してくれて、目的のデータが見つかったら教えてくれるんです。
まるで、広大な図書館の中から、目的の本を素早く見つけ出してくれる司書さんのようですね。
SEARCH文を使いこなせれば、プログラムの処理速度を上げることができ、スマートなコーディングが可能になりますよ。
「SEARCH文」の基本的な書き方(構文)
では、実際にSEARCH文をどうやって書くのか、基本的な形を見てみましょう。
細かいオプションは一旦置いておいて、まずは骨組みを掴むことが肝心です。
SEARCH テーブル名 AT END 見つからなかった時の処理 WHEN 条件 見つかった時の処理 END-SEARCH.
ざっくりと、こんな形になります。
それぞれの部分が何を意味するか、簡単に説明しますね。
- SEARCH テーブル名
「これから『テーブル名』という名前のテーブルを探しますよー!」という宣言です。 - AT END
テーブルの最後まで探したけど、結局条件に合うデータが見つからなかった…という場合に、ここ(AT ENDからWHENまでの間)に書かれた処理が実行されます。 - WHEN 条件
「こんな条件に合うデータを探してね!」という指定です。条件に合うデータが見つかったら、ここ(WHENからEND-SEARCHまでの間、または次のWHENまで)に書かれた処理が実行されます。 - END-SEARCH
「SEARCH文はここまでですよ」という終わりの合図です。
これがSEARCH文の基本的な構造です。
次は、もう少し具体的に、SEARCH文の2つの種類「逐次サーチ」と「二分探索サーチ」の書き方を見ていきましょう。
逐次サーチ(Sequential SEARCH)の構文
まずは「逐次サーチ」を行うSEARCH文です。
逐次サーチは、テーブルの先頭から順番に、一つずつ条件に合うかチェックしていく方法です。シンプルで分かりやすいですね。
書き方はこんな感じです。
SET 指標名 TO 1. <-- 検索開始位置を先頭(1番目)にセット! SEARCH テーブル名 VARYING 指標名 AT END *> 見つからなかった時の処理をここに書く DISPLAY "データが見つかりませんでした。" WHEN テーブル要素(指標名) = 探したい値 *> 見つかった時の処理をここに書く DISPLAY "データが見つかりました!" *> (必要なら見つかった要素に対する処理などを書く) END-SEARCH.
いくつか新しい要素が出てきましたね。
- SET 指標名 TO 1.
これが超重要! 逐次サーチを始める前に、必ずテーブルのどこから探し始めるか(通常は先頭の1番目)を`SET`文で指定してあげる必要があります。これを忘れると、予期せぬ動きをしたり、エラーになったりするので注意してくださいね。`SET`文での指標の初期化は忘れずに! - VARYING 指標名
SEARCH文がテーブルの中を移動していく際に、現在位置を示す「指標(インデックス)」を動かすための指定です。SEARCH文が内部で指標の値を増やしながら検索を進めてくれます。 - テーブル要素(指標名) = 探したい値
これが`WHEN`句の具体的な条件の書き方の一例です。「テーブルの『指標名』番目の要素が、『探したい値』と同じだったら」という意味になります。
逐次サーチは、テーブルのデータが順番に並んでいなくても使えますが、データ量が多いと時間がかかることがあります。
二分探索サーチ(SEARCH ALL)の構文
次にご紹介するのが「二分探索サーチ」、`SEARCH ALL`文です。
これは、逐次サーチよりもずっと高速に検索できる可能性がある、賢い方法なんです!
ただし、`SEARCH ALL`を使うには事前の準備が必要不可欠です。
それは、
「検索対象のテーブルが、検索キーとなる項目で、あらかじめ昇順(小さい順)または降順(大きい順)に並べ替えられていること」
です。これが守られていないと、正しく動作しません。
イメージとしては、辞書で単語を探す時、最初から順番にページをめくる(逐次サーチ)のではなく、真ん中あたりを開いて、目的の単語がそれより前にあるか後ろにあるか判断し、探す範囲をどんどん半分に絞っていく(二分探索サーチ)感じです。だから速いんですね!
書き方はこちら。
SEARCH ALL テーブル名 AT END *> 見つからなかった時の処理をここに書く DISPLAY "データが見つかりませんでした。" WHEN キー項目(指標名) = 探したい値 *> 見つかった時の処理をここに書く DISPLAY "データが見つかりました!" *> (必要なら見つかった要素に対する処理などを書く) END-SEARCH.
逐次サーチの`SEARCH`と比べて、`SEARCH ALL`には`VARYING`句がありませんね。
また、`WHEN`句の条件で使う項目は、テーブル定義時に`ASCENDING KEY`または`DESCENDING KEY`として指定した「キー項目」である必要があります。(テーブル定義については後ほど説明しますね!)
そして、`SEARCH ALL`では、`SET`文による指標の初期化は不要です。COBOLが自動で効率的な検索を行ってくれます。
データ量が多い場合や、検索速度が求められる場合には、`SEARCH ALL`が非常に有効です。ただし、事前にテーブルをソートしておく手間がかかる点を覚えておきましょう。
「SEARCH文」を使うための準備:テーブル定義
さて、SEARCH文を使うには、まず検索対象となる「テーブル(配列)」をデータ部(DATA DIVISION)で定義しておく必要があります。
これは料理で言う「下ごしらえ」のようなもの。SEARCH文という調理器具を使うために、材料(データを入れる器=テーブル)を用意するイメージです。
テーブル定義で特にSEARCH文と関わりが深いのが、`OCCURS`句と`INDEXED BY`句です。
OCCURS句によるテーブル定義
`OCCURS`句は、「この項目を〇回繰り返しますよ」と指定するためのものです。
これを使うことで、同じ形のデータ項目を複数個まとめたテーブル(配列)を作ることができます。
例えば、社員データを100人分格納するテーブルを作りたい場合は、こんな風に書きます。
DATA DIVISION. WORKING-STORAGE SECTION. 01 SYAIN-TABLE. 05 SYAIN-DATA OCCURS 100 TIMES. <-- ここ! SYAIN-DATAを100回繰り返す 10 SYAIN-NO PIC 9(5). <-- 社員番号 (5桁の数字) 10 SYAIN-NAME PIC X(20). <-- 氏名 (20桁の文字)
これで、`SYAIN-DATA`という社員1人分のデータ構造(社員番号と氏名)が100個並んだ`SYAIN-TABLE`というテーブルが用意できました。
テーブルの大きさを決める大切な句です。SEARCH文は、この`OCCURS`句で定義された範囲内を検索します。
INDEXED BY句による指標名の設定
次に`INDEXED BY`句です。これは、SEARCH文がテーブル内を移動(検索)する際に、「今、テーブルの何番目にいるのか」を示すための特別な変数(指標、インデックス)の名前を決めるものです。
先ほどのテーブル定義に`INDEXED BY`句を追加してみましょう。
DATA DIVISION. WORKING-STORAGE SECTION. 01 SYAIN-TABLE. 05 SYAIN-DATA OCCURS 100 TIMES INDEXED BY SYAIN-INDEX. <-- ここ! 10 SYAIN-NO PIC 9(5). 10 SYAIN-NAME PIC X(20).
このように`INDEXED BY SYAIN-INDEX`と書くことで、`SYAIN-INDEX`という名前の指標が使えるようになります。
この指標名は、逐次サーチの`SET`文や`VARYING`句、そして`WHEN`句の条件で `テーブル要素(指標名)` のように使われます。
指標はSEARCH文の相棒のような存在で、`SEARCH`文を使うためには、原則として`INDEXED BY`句で指標名を指定しておく必要があります。
さらに! `SEARCH ALL`文を使いたい場合は、これに加えて、どの項目を検索キーにするかを指定する`ASCENDING KEY IS`句(昇順の場合)または`DESCENDING KEY IS`句(降順の場合)も必要になります。
DATA DIVISION. WORKING-STORAGE SECTION. 01 SYAIN-TABLE. 05 SYAIN-DATA OCCURS 100 TIMES ASCENDING KEY IS SYAIN-NO <-- SEARCH ALL用のキー指定(昇順) INDEXED BY SYAIN-INDEX. <-- 指標名の指定 10 SYAIN-NO PIC 9(5). 10 SYAIN-NAME PIC X(20).
これで、`SYAIN-NO`(社員番号)をキーとして`SEARCH ALL`文が使えるようになりました!
「SEARCH文」の使い方
お待たせしました!いよいよ、実際にSEARCH文を使った簡単なプログラムを見ていきましょう。
百聞は一見に如かず、コードを動かしてみるのが一番の近道です。
ここでは、逐次サーチ(`SEARCH`)と二分探索サーチ(`SEARCH ALL`)の両方のサンプルを用意しました。
ぜひ、お手元の環境でコピーして試してみてくださいね。
逐次サーチ(SEARCH)のサンプルと実行結果
まずは、逐次サーチのサンプルです。
商品コード(`PRODUCT-CODE`)が3つの商品データ(`PRODUCT-DATA`)を持つテーブル(`PRODUCT-TABLE`)から、指定した商品コードを探します。
サンプルプログラム (Sequential Search)
IDENTIFICATION DIVISION. PROGRAM-ID. SEQ-SEARCH-SAMPLE. DATA DIVISION. WORKING-STORAGE SECTION. 01 SEARCH-CODE PIC X(3) VALUE "B01". *> 探したい商品コード 01 MSG PIC X(60). 01 PRODUCT-TABLE. 05 PRODUCT-DATA OCCURS 3 TIMES INDEXED BY P-INDEX. 10 PRODUCT-CODE PIC X(3). 10 PRODUCT-NAME PIC X(10). PROCEDURE DIVISION. *> テーブルにデータを設定 (サンプルなので直接値を設定) MOVE "A01" TO PRODUCT-CODE(1). MOVE "Apple" TO PRODUCT-NAME(1). MOVE "B01" TO PRODUCT-CODE(2). MOVE "Banana" TO PRODUCT-NAME(2). MOVE "C01" TO PRODUCT-CODE(3). MOVE "Cherry" TO PRODUCT-NAME(3). *> 検索開始 SET P-INDEX TO 1. *> 指標を初期化! SEARCH PRODUCT-DATA VARYING P-INDEX AT END MOVE "商品が見つかりませんでした。" TO MSG WHEN PRODUCT-CODE(P-INDEX) = SEARCH-CODE STRING "商品が見つかりました! Name:" DELIMITED BY SIZE PRODUCT-NAME(P-INDEX) DELIMITED BY SIZE INTO MSG END-SEARCH. *> 結果表示 DISPLAY MSG. STOP RUN.
実行結果 (SEARCH-CODE が "B01" の場合)
商品が見つかりました! Name:Banana
実行結果 (SEARCH-CODE が "D01" の場合)
商品が見つかりませんでした。
コードのポイント
- `WORKING-STORAGE SECTION`でテーブル`PRODUCT-TABLE`と指標`P-INDEX`を定義しています。`OCCURS 3 TIMES`で要素数は3つです。
- `PROCEDURE DIVISION`の最初で、テーブルにサンプルデータを`MOVE`文で設定しています。
- `SEARCH`文の前に、必ず`SET P-INDEX TO 1.`で指標を初期化しています。これが大事!
- `WHEN`句で`PRODUCT-CODE(P-INDEX)`と`SEARCH-CODE`を比較し、一致したらメッセージを設定しています。見つかった要素の`PRODUCT-NAME(P-INDEX)`もメッセージに含めています。(この部分の`STRING`文も、前回の`SEARCH ALL`の例と同様の注意点があります。より安全な書き方は`SEARCH ALL`の修正例を参考にしてください。)
- `AT END`句で、見つからなかった場合のメッセージを設定しています。
コード内のコメントも参考にしてくださいね。 逐次サーチの基本的な流れが掴めたでしょうか?
二分探索サーチ(SEARCH ALL)のサンプルと実行結果
続いて、二分探索サーチ(`SEARCH ALL`)のサンプルです。
今度は、社員番号(`SYAIN-NO`)でソートされた社員テーブル(`SYAIN-TABLE`)から、指定した社員番号を探します。(前回指摘のあった`STRING`文を修正済みです)
サンプルプログラム (Binary Search - 修正版)
IDENTIFICATION DIVISION. PROGRAM-ID. BIN-SEARCH-SAMPLE-MOD. DATA DIVISION. WORKING-STORAGE SECTION. 01 SEARCH-NO PIC 9(5) VALUE 00015. *> 探したい社員番号 01 MSG PIC X(60). 01 SYAIN-TABLE. 05 SYAIN-DATA OCCURS 5 TIMES ASCENDING KEY IS SYAIN-NO *> キー項目(昇順)を指定 INDEXED BY S-INDEX. *> 指標を指定 10 SYAIN-NO PIC 9(5). 10 SYAIN-NAME PIC X(20). PROCEDURE DIVISION. *> テーブルにデータを設定 (昇順にソートされている必要がある!) MOVE 00005 TO SYAIN-NO(1). MOVE "Sato" TO SYAIN-NAME(1). MOVE 00010 TO SYAIN-NO(2). MOVE "Suzuki" TO SYAIN-NAME(2). MOVE 00015 TO SYAIN-NO(3). MOVE "Takahashi" TO SYAIN-NAME(3). MOVE 00020 TO SYAIN-NO(4). MOVE "Tanaka" TO SYAIN-NAME(4). MOVE 00025 TO SYAIN-NO(5). MOVE "Watanabe" TO SYAIN-NAME(5). *> 検索開始 (SEARCH ALLではSETによる指標の初期化は不要) SEARCH ALL SYAIN-DATA AT END MOVE "社員が見つかりませんでした。" TO MSG WHEN SYAIN-NO(S-INDEX) = SEARCH-NO *> キー項目で比較! STRING "社員が見つかりました! Name:" DELIMITED BY SIZE SYAIN-NAME(S-INDEX) DELIMITED BY SIZE INTO MSG END-SEARCH. *> 結果表示 DISPLAY MSG. STOP RUN.
実行結果 (SEARCH-NO が 00015 の場合)
社員が見つかりました! Name:Takahashi
実行結果 (SEARCH-NO が 00011 の場合)
社員が見つかりませんでした。
コードのポイント
- テーブル定義で`ASCENDING KEY IS SYAIN-NO`と`INDEXED BY S-INDEX`が指定されています。これが`SEARCH ALL`の必須条件です。
- テーブルに設定するデータは、必ず`SYAIN-NO`の昇順になるようにしています。(00005, 00010, 00015...)
- `SEARCH ALL`文の前には、`SET`文による指標の初期化は書いていません。不要だからです。
- `WHEN`句の条件では、キー項目として指定した`SYAIN-NO(S-INDEX)`と`SEARCH-NO`を比較しています。
- `WHEN`句の処理を修正しました。`STRING`文で固定文字列と`SYAIN-NAME(S-INDEX)`を直接`MSG`に連結するように変更し、不要な`MOVE`文を削除(コメントアウト)しました。
逐次サーチとのコードの違いを見比べてみましょう。
特にテーブル定義と`SET`文の有無に注目してください。`SEARCH ALL`がいかに事前準備(ソートとキー指定)を前提としているかが分かりますね。
COBOLの「SEARCH文」を使う上での注意点
SEARCH文はとても便利ですが、いくつか気をつけておきたいポイントがあります。
特に初心者のうちは、思わぬエラーに繋がることもあるので、しっかり押さえておきましょう!
SEARCH ALLを使う際のキー項目のソート
これは`SEARCH ALL`を使う上で、絶対に守らなければならないルールです。
先ほども説明しましたが、`SEARCH ALL`文は、テーブルのデータが`ASCENDING KEY`または`DESCENDING KEY`で指定したキー項目に関して、きちんと昇順または降順にソート(整列)されていることを前提として動作します。
もし、ソートされていないテーブルに対して`SEARCH ALL`を使ってしまうと…
- 探しているデータがあるはずなのに「見つからない」という結果になる。
- 全く関係ないデータが「見つかった」ことになってしまう。
- 場合によっては、プログラムが異常終了してしまう。
といった、予期せぬ結果を招きます。
なぜソートが必要かというと、`SEARCH ALL`(二分探索)は、「今の場所のキーの値」と「探している値」を比べて、「もっと前を探すべきか、後ろを探すべきか」を判断しながら検索範囲を絞り込んでいく仕組みだからです。データがバラバラの順番だと、この大小比較が正しく機能しないんですね。
ですから、`SEARCH ALL`を使う前には、必ずテーブルが正しくソートされているかを確認しましょう。もしソートされていない場合は、`SORT`文などを使って事前にソート処理を行う必要があります。ソートされていないと正しい結果が出ません!
【まとめ】COBOLの「SEARCH文」をマスターして効率的な検索を!
お疲れ様でした!今回はCOBOLのテーブル検索の強い味方、「SEARCH文」について、基本的な役割から、逐次サーチ(`SEARCH`)と二分探索サーチ(`SEARCH ALL`)の書き方、使い方、そして注意点まで、盛りだくさんで解説してきました。
もう一度、ポイントをおさらいしましょう。
- SEARCH文はテーブルから効率的にデータを探す命令。
- 逐次サーチ(`SEARCH`)は先頭から順番に探す。指標の初期化(`SET`)が必要。
- 二分探索サーチ(`SEARCH ALL`)は高速だが、キー項目での事前ソートが必須。テーブル定義でキーと指標の指定が必要。
- `OCCURS`句でテーブルの大きさを、`INDEXED BY`句で指標を定義する。
- `SEARCH ALL`を使う前は、必ずソートされているか確認!
最初は少し複雑に感じるかもしれませんが、サンプルコードを実際に動かしてみたり、自分で簡単なプログラムを作ってみたりするうちに、きっとSEARCH文の便利さが実感できるはずです。
SEARCH文をマスターすれば、COBOLでのデータ処理がもっとスムーズに、そしてプログラムの品質もグッと向上しますよ。
この記事が、あなたのCOBOL学習の一助となれば、とても嬉しいです。
自信を持って、どんどんSEARCH文を使ってみてください!応援しています!
【関連記事】
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。