DL/I の例

以下の DL/I の例は、『サンプル DL/I データベース』で説明したものと同じ サンプル DL/I データベースを使用しています。以下に、DL/I データベース I/O の 代表的な技法の例をいくつか記載しています。

親セグメント内での検索

デフォルトの get next ステートメントは、データベースの現在位置で開始し、すべての親の下にあるターゲット・セグメントの次のオカレンスについてデータベース全体を検索します。 検索の範囲を、現時点で確立されている従属チェーンに限定するには 、get next inParent ステートメントを使用します。

特定のオーダーの項目を取得する状況について考えてみましょう。 以下を使用して、オーダーを検索することができます。
get myOrder with #dli {
   GU STSCCST (STQCCNO = :myCustomer.customerNo)
      STSCLOC (STQCLNO = :myLocation.locationNo)
      STSOCORD (STQCODN = :myOrder.orderDateno) };
次に、以下を使用すると、オーダー内にある行セグメントを取得することができます。
get next inParent myItem;
  while (myItem not noRecordFound)
  // process the current item
  get next inParent myItem;
end
EGL は、get next inParent ステートメントに関する以下の 疑似 DL/I コードを作成します。
GNP  STLCITM 

別の非キー・フィールドによる検索

呼び出しの検索引数 (SSA) を変更することにより、セグメントの任意のフィールドを、DL/I 呼び出しでの検索引数として使用することができます。 例えば、カスタマー・データベースを読み取って、預金残高が指定金額よりも多い顧客ごとに、 カスタマー・セグメントおよびクレジット・セグメントを取得する場合、DL/I 呼び出し検索引数を 以下のように定義します。
  1. クレジット・セグメント (STSCSTA) の creditBalance フィールド (STFCSBL) で検索が必要です。 これを実行するには、検索する指定金額を含む、型 decimal(12,2) の変数 (targetBalance など) を定義します。 targetBalance の定義は、クレジット・セグメントの creditBalance フィールドの定義と 一致させる必要があります。
  2. myCrStatus レコード用に get next ステートメントを書き込みます。
  3. #dli ディレクティブをその行に追加し、デフォルト・コードを変更します。creditBalance フィールドの金額が targetBalance よりも大きいセグメントを探す、 修飾 SSA を追加します。
  4. パス・コマンド・コード (*D) を組み込んで、クレジット・セグメントに対応するカスタマー・セグメント (STSCCST) を取り出します。
次のサンプル・コードは、このプロセスを説明したものです。
	targetBalance decimal(12,2);
	targetBalance = 10,000.00;

	get next myCustomer,myCrStatus with #dli{
		GU STSCCST*D STSCSTA (STFCSBL >= :targetBalance) };

別のレコードの情報に基づいた検索

別のレコードの情報に基づいて、検索を実行することもできます。以下のように、 カスタマー番号が、パラメーター・レコードのプログラムに渡される状態を考えてみます。
Record CustomerData type basicRecord 
  10 customerNo char(6) 
  ... 
end

Program myProgram 
  (customerParm  CustomerData)
  { @DLI{ psb = "myCustomerPSB" }} 
  //declare variables 
  myCustomerPSB     CustomerPSBRecordPart; 
  myCustomer        CustomerRecordPart;
customerParm.customerNo の値に基づいて、カスタマー・セグメントを取得する必要があります。 以下のように、これをコード化する 3 つの方法があります。
  • 以下のように、customerParm.customerNo の値 を myCustomer.customerNo に割り当ててから、暗黙的に DL/I I/O を使用します。
    myCustomer.customerNo = CustomerParm.customerNo;
    get myCustomer;
    この場合、EGL は、通常のデフォルト SSA を作成し、その結果として次の疑似 DL/I コードが作成されます。
    GU STSCCST (STQCCNO = :myCustomer.customerNo)
    この方法の長所は、大変シンプルである点です。 欠点は、カスタマー番号をセグメント・レコードに移動するというパフォーマンス上の オーバーヘッドがわずかに発生する点です。
  • 以下のように、#dli ディレクティブおよび明示的 DL/I I/O を使用すると、customerParm.customerNo フィールドをホスト変数として使用できます。
    get myCustomer with #dli {
       GU  STSCCST (STQCCNO = :customerParm.customerNo) } ;
    この方法では、 カスタマー番号を移動するというパフォーマンス上のオーバーヘッドが発生しません。 ただし、デフォルトの #dli ディレクティブに貼り付けてから、パラメーター・レコードに合わせて 正しいレコード変数名を使用するようにコードを変更するのに、やや時間がかかります。
  • 以下のように、DLISegment レコード に hostVarQualifier プロパティーを追加して、ホスト変数の修飾子を指定します。
    Record CustomerRecordPart type DLISegment 
      {segmentName="STSCCST", keyItem="customerNo", 
       hostVarQualifier="customerParm" }
      10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field 
      ... 
    end
    この後は、以下のように 暗黙的に DL/I I/O を使用することができ、 最初に customerParm.customerNomyCustomer.customerNo に割り当てる必要はありません。
    get myCustomer;
    この場合、EGL が作成する疑似 DL/I コードは以下のとおりです。
    GU STSCCST (STQCCNO = :customerParm.customerNo)

    この方法の長所は、#dli ディレクティブ用にコード化したものと同じ疑似 DL/I コードが得られ、実際に #dli ディレクティブをコード化する必要がない点です。欠点は、EGL が、CustomerRecordPart を使用する 暗黙的 DL/I データベース I/O ステートメントごとに、customerParm を修飾子として使用するようになる点です。

非ルート・セグメントの検索

暗黙的 DL/I データベース I/O を使用して、非ルート・セグメントを検索できます。 EGL は、PCB でのターゲット・セグメントの階層ポジションに基づいて、SSA のチェーン全体を作成します。 ただし、階層のより高いレベルにあるセグメントについては、EGL は、プログラム変数の名前を自動的に決定してセグメントの修飾子として使用することはできません。

カスタマー・セグメント STSCCST がロケーション・セグメント STSCLOC の親で、そのロケーション・セグメントのデータのみを検索したい状態を考えてみましょう。 プログラムで、以下のレコード変数を定義しました。
myCustomer CustomerRecordPart; 
myLocation LocationRecordPart;
以下の get ステートメントを使用する場合:
get myLocation;
EGL は、以下の疑似 DL/I コードを作成します。
GU STSCCST (STQCCNO = :CustomerRecordPart.customerNo) 
  STSCLOC (STQCLNO = :myLocation.locationNo)

EGL は変数 myLocation をセグメント STSCLOC に正しく関連付けているものの、一方で EGL は、CustomerRecordPart に基づいている変数 myCustomer を検出できなかった点に注意してください。 EGL が認識しているのは、myLocation のタイプが LocationRecordPart であり、その親セグメントは CustomerRecordPart であること、 また CustomerRecordPart の keyItemcustomerNo であることのみです。

この問題は、以下のようないくつかの方法で解決できます。
  • レコード変数に、そのレコード変数が基づいているレコード・パーツと同じ名前を付けることができます。 例えば、myCustomer の変数宣言を CustomerRecordPart に変更します。
    CustomerRecordPart CustomerRecordPart;
    次のように、EGL は同じ 疑似 DL/I コードを作成します。
    GU STSCCST (STQCCNO = :CustomerRecordPart.customerNo) 
      STSCLOC (STQCLNO = :myLocation.locationNo)

    EGL は、PCB 階層情報からデフォルトの SSA を作成するので、この方法で簡単に、EGL が使用する変数名をご使用のプログラムのレコード変数宣言と必ず 一致させることができます。 欠点として、この手法は、パーツおよび変数に別の名前を使用するという、一般的な慣例に従わない点があります。

  • 次のように、#dli ディレクティブおよび明示的 DL/I データベース I/O を使用します。
    get myLocation with #dli {
      GU STSCCST (STQCCNO = :myCustomer.customerNo)
        STSCLOC (STQCLNO = :myLocation.locationNo)

    この手法の長所は、使用されているホスト変数が大変わかりやすい点です。 ホスト変数修飾子またはフィールド名が、DL/I セグメント・レコードに基づくレコード変数と異なる場合には、簡単に使用できる手法です。 欠点は、すべての明示的 I/O の場合と同様です。つまり、階層またはキー・フィールドが変更されても、明示的 DL/I データベース I/O ステートメントは、自動的に変更されません。

  • 次のように、CustomerRecordPart の定義を変更して 、hostVarQualifier プロパティーを組み込みます。
    Record CustomerRecordPart type DLISegment 
      { segmentName="STSCCST", keyItem="customerNo",
        hostVarQualifier = "myCustomer" } 
      10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field 
      ... 
    end
    ここで、以下の get ステートメントを使用する場合:
    get myLocation;
    EGL は、以下の正しい 疑似 DL/I コードを生成します。
    GU STSCCST (STQCCNO = :myCustomer.customerNo) 
      STSCLOC (STQCLNO = :myLocation.locationNo)

    この手法の長所は、暗黙的 DL/I データベース I/O を使用できることと、レコード変数および DL/I セグメント・レコードに別の名前を指定することができる点です。 欠点は、EGL が、CustomerRecordPart を使用する暗黙的 DL/I データベース I/O ステートメントごとに、myCustomer を修飾子として使用するようになる点です。

2 次索引による検索

場合によって、DL/I PSB は、データベース構造が 2 次索引でアクセスされている ことを示すことがあります。 この状態が発生したときには、データベース管理者は、ランタイム PCB 定義の ルート・セグメントの隣に、索引キー名を入力します。 secondaryIndex プロパティーを PSBRecord 内の PCB レコードに組み込むことで、2 次索引の使用について EGL に通知します。 例えば、customerName が 2 次索引であるカスタマー・データベースのビューが他にもある場合は、次のように、PSB レコード・パーツにさらに PCB レコードを追加できます。
// database PCB for customer by Name
customerNamePCB DB_PCBRecord 
  { @PCB { pcbType = DB, pcbName = "STDCNAM", 
    secondaryIndex = "STUCCNM",
    hierarchy = [ @relationship { segmentRecord = "CustomerRecordPart" },
    ... ] }
  }

pcbName プロパティーは、ランタイム PSB の実際 の DL/I データベース PCB と一致させる必要があります。 secondaryIndex プロパティーには、データベース管理者 が、ランタイム PCB の XDFLD ステートメントで指定するフィールド名と同じものを指定する必要があります。ここで、階層に CustomerRecordPart を含む PSB レコードには 、2 つの PCB レコードが存在します。

get ステートメントを発行して顧客を見つける場合は、次の例に従います。
myCustomer CustomerRecordPart;
get myCustomer;
EGL は、CustomerRecord パーツを含む PSB レコードの最初の PCB レコードに基づいて、疑似 DL/I コードを作成します。
2 次索引データベースが PSB レコードの 2 番目の PCB レコードである場合 、以下の例にあるように、usingPCB キーワードを組み込んで、EGL が正しい PCB を使用するようにする必要があります。
get myCustomer usingPCB customerNamePCB;
EGL は、以下の疑似 DL/I コードを作成します。
GU STSCCST (STUCCNM = :myCustomer.customerName)
EGL が、get ステートメントのレコード変数 名 (myCustomer) と、usingPCB キーワードで指定 された PCB (customerNamePCB) を使用して、以下の情報を検出した点に注意してください。
  • セグメントの DL/I 名 (プロパティー segmentName = "STSCCST")
  • 2 次索引フィールドの DL/I 名 (プロパティー secondaryIndex = "STUCCNM")
  • 比較値の EGL EGL フィールド名 (プロパティー dliFieldName = "STUCCNM" と対応するフィールド名 customerName)
場合によっては、物理データベースのより低いレベルのセグメントに、2 次索引があることがあります。 その場合、データベース構造は反転します。 例えば、オーダー・セグメントの orderReference フィールドに 2 次索引がある場合、以下のように、プログラムで表示されるデータベース階層を、対応する PCB レコードに反映させる必要があります。
// orders view of customer database 
ordersByReferencePCB DB_PCBRecord 
  { @PCB { pcbType = DB, pcbName = "STDCDBL", 
    secondaryIndex = "STFCORF",   //use DL/I name 
    hierarchy = [ 
        @relationship { segmentRecord = "OrderRecordPart" }, 
        @relationship { segmentRecord = "LocationRecordPart", 
                                    parentRecord = "OrderRecordPart" }, 
        @relationship { segmentRecord = "CustomerRecordPart", 
                                    parentRecord = "LocationRecordPart" }, 
        @relationship { segmentRecord = "ItemRecordPart", 
                                    parentRecord = "OrderRecordPart" }
        ]}
  }; 
end 
オーダー参照番号がそれぞれの顧客およびオーダーに対して固有であると仮定すれば、ロケーション・セグメントおよびカスタマー・セグメントのキーを使用する必要はありません。 ordersByReferencePCB がデフォルトの PCB であるとすると、以下のように、ロケーション・セグメントとカスタマー・セグメントに関する比較を削除するように SSA を変更することで、 オーダーと顧客の両方を検索できます。
get myOrder, myCustomer with #dli{ 
  GU STPCORD*D (STQCODN = :myOrder.orderReference) 
    STSCLOC 
    STSCCST };

パス呼び出しを使用した複数セグメントへのアクセス

従属セグメントのレコード・オブジェクトを指定して EGL I/O ステートメントを呼び出す場合、同時に、オブジェクトへのルートのパスで、セグメントの任意の場所に読み込むことができます。 これは、パス呼び出しのレコードをステートメントに追加するだけで行うことができます。 例えば、オーダー・セグメントを、サンプル・カスタマー・データベースから取得するときには、以下のように、同じ呼び出しで、カスタマー、ロケーション、およびオーダー・セグメントに読み込むことができます。
get myCustomer, myLocation, myOrder;
このステートメントは、以下の DL/I 疑似コードを生成します。
GU STSCCST*D (STQCCNO = :myCustomer.customerNo) 
   STSCLOC*D (STQCLNO = :myLocation.locationNo)
   STPCORD   (STQCDDN = :myOrder.orderDateNo)
D コマンド・コードを get...forUpdate ステートメントで使用する場合、後続の replace ステートメントは検索されるすべてのセグメントに影響します。 replace キーワードに、SSA の明示的 N コマンド・コードを指定することで、 選択されたセグメントが置き換えられないようにすることができます。以下はその例で、ここでは、ロケーションおよびオーダー・セグメントのみを置き換えています。
get myCustomer, myLocation, myOrder forUpdate;
replace myOrder with #dli{
	REPL STSCCST*N
	     STSCLOC*N
       STPCORD };
D コマンド・コードを指定した get forUpdate ステートメントに従う delete 関数に対するデフォルトの DL/I 呼び出し EGL ビルドは、検索される各セグメントを削除しません。 delete ステートメントで指定されたターゲット・セグメントのみを削除します。

単一 I/O ステートメントによる全セグメントの読み取り

単一の I/O ステートメントを使用して、データベースにあるすべてのセグメントを読み取ることができます。 SSA を指定せずに DL/I get next ステートメントを実行した場合、DL/I は、セグメントのタイプとは無関係に、データベースの次のセグメントを戻します。この手法を使用するには、以下のステップに従ってください。
  1. データベースで最大のセグメントを表すレコードに、get next ステートメントを書き込む。 これにより、読み取るセグメントはいずれも、割り振られたメモリーを超過することはありません。
  2. デフォルトの DL/I 呼び出しをコードに追加する。#dli ディレクティブを編集して、1 つ の SSA を削除します。
  3. データベースのその他のセグメントに一致するレコードを作成する。プログラムでそのレコードを宣言し、それぞれのレコードが、上記のステップ 1 で使用したレコードを再定義し、すべてのレコードがメモリーの同じ領域を占めるように指定します。
  4. get next ステートメントの後ろの dliVar.segmentName を確認し、検索されたセグメントのタイプを判別する。
  5. 対応するレコード構造から検索されたセグメントにアクセスする。
以下は、カスタマー・データベースのすべてを印刷するコードの例です。 この例では、HistoryRecordPart が最大の DLISegment レコードです。
myHistory HistoryRecordPart
redefCustomer CustomerRecordPart {redefines=myHistory};
redefLocation LocationRecordPart {redefines=myHistory};
...


//read next segment, whatever type it is, into history record
while (myHistory not EOF)
	get next myHistory with #dli{
		GN };

	//so what type was it?
	case (dliVar.segmentName)
		when "STSCCST"                   // it was a customer
			printCustomer();
		when "STSCLOC"                   // it was a location
			printLocation();
		...
	end
end

動的配列の使用

get ステートメント および get next ステートメントを使用して 、動的配列に対して DL/I セグメントを検索できます。配列 そのもの (配列のメンバーについてのみ) にキー・フィールドはないので、いくつかの特殊な手法 を使用して、EGL が正しいコードを作成できるようにする必要があります。

次のように、レコード変数および動的配列を定義した状態を考えてみましょう。
myCustomer CustomerRecordPart;
myLocation LocationRecordPart;
myOrder OrderRecordPart;
myOrderArray OrderRecordPart [] {maxsize = 20};  // オーダーの配列
次のように、get ステートメントを使用してまず配列を埋め、get next ステートメントを使用して、20 オーダーの 第 2 グループを検索することができます。
myCustomer.customerNo = "123456";
myLocation.locationNo = "ABCDEF";
myOrderDateNo = "20050730A003";
get myOrderArray;                  // 最初に配列を埋める
... do some processing
get next myOrderArray;          // 20 オーダーの次のバッチを取得する
EGL は、get ステートメントについて、次の疑似 DL/I コードを作成します。
get myOrderArray with #dli{
  GU STSCCST (STQCCNO = :CustomerRecordPart.customerNo)
    STSCLOC (STQCLNO = :LocationRecordPart.locationNo)
    STPCORD (STQCODN = :OrderRecordPart.orderDateNo)
  GN STPCORD };
EGL は、get next ステートメントについて、 次の疑似 DL/I コードを作成します。
get next myOrderArray with #dli{
  GN STPCORD };

20 オーダーの最初のバッチに対する動的配列を get ステートメントによって埋めるには、初回の GU 呼び出しを行った後で、配列がフルになるか、 または DL/I がセグメント・オカレンスをすべて検索するまで、GN 呼び出しをループさせる必要があります。 EGL が作成する疑似 DL/I コードで、GU 呼び出しは、最初のオーダー・セグメントを検索します。 EGL は、GN 呼び出しをループとして扱い、配列がフルになるか、DL/I がセグメント・オカレンスをすべて検索するまで、ループするロジックを提供します。 同様に、EGL は get next ステートメントをループとして扱い、 ループ制御ロジックをユーザーに提供します。

get next ステートメントは、正しい疑似 DL/I コードを提供して、オーダーの 2 番目のバッチを検索します。 ただし、get ステートメントの疑似 DL/I コードは、厳密には 正しくありません。ホスト変数の修飾子の名前は、プログラムのレコード変数の名前ではありません。 この問題は、以下のようないくつかの方法で解決できます。
  • DL/I セグメント・レコードに、DL/I データベースのセグメントと同じ名前を付けます。また、レコード変数名を、DL/I データベースのセグメントと同じ名前に変更します。例えば、以下のカスタマー・レコード・パーツの定義を変更します。
    Record CustomerRecordPart type DLISegment 
      { segmentName="STSCCST", keyItem="customerNo" } 
      10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field 
      ... 
    end
    この関数を、次のように変更します。
    Record STSCCST type DLISegment 
      { segmentName="STSCCST", keyItem="customerNo" } 
      10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field 
      ... 
    end
    ロケーション・セグメント (STSCLOC) およびオーダー・セグメント (STPCORD) に対して、同様の変更を行います。 その後、次のように PSB レコードを変更して、新規名を使用するようにします。
    Record CustomerPSBRecordPart type PSBRecord { defaultPSBName="STBICLG" } 
      // database PCB 
      customerPCB DB_PCBRecord { @PCB { pcbType = DB, pcbName = "STDCDBL", 
      hierarchy = [ @relationship { segmentRecord = "STSCCST" },
           @relationship {segmentRecord="STSCLOC",
                          parentRecord="STSCCST"},
           @relationship {segmentRecord="STPCORD",
                          parentRecord="STSCLOC"}
           ]}};
    
    end
    次のように、レコード変数および myOrderArray の宣言を変更して、 新規の DL/I セグメント・レコード・パーツを参照するようにします。
    STSCCST STSCCST;
    STSCLOC STSCLOC;
    STPCORD STPCORD;
    myOrderArray STPCORD [] {maxsize = 20};  // array of orders
    ここで、以下の get ステートメントを実行する場合:
    get myOrderArray;                  // 最初に配列を埋める
    EGL は、get ステートメントに対して以下の疑似 DL/I コードを作成し、ホスト変数修飾子は、正しいレコード変数名を使用します。
    get myOrderArray with #dli{
      GU STSCCST (STQCCNO = :STSCCST.customerNo)
        STSCLOC (STQCLNO = :STSCLOC.locationNo)
        STPCORD (STQCODN = :STPCORD.orderDateNo)
      GN STPCORD };

    EGL は、PCB 階層情報からデフォルトの SSA を作成するので、この方法で簡単に、EGL が使用する変数名をご使用のプログラムのレコード変数宣言と必ず 一致させることができます。 欠点として、この手法は、パーツおよび変数に別の名前を使用するという、一般的な慣例に従わない点があります。

  • 次のように、#dli ディレクティブおよび明示的 DL/I データベース I/O を使用します。
    get myOrderArray with #dli{
      GU STSCCST (STQCCNO = :myCustomer.customerNo)
        STSCLOC (STQCLNO = :myLocation.locationNo)
        STPCORD (STQCODN = :myOrder.orderDateNo)
      GN STPCORD };
    この手法の長所は、使用されているホスト変数が大変わかりやすい点です。 この手法は、デフォルトの DL/I コードを変更する必要がある場合に特に有効です。例えば、顧客の最初のオーダー番号が不明で、以下の DL/I コードを使用したい場合などです。
    myOrder.orderDateNo = "";
    get myOrderArray with #dli{
      GU STSCCST (STQCCNO = :myCustomer.customerNo)
        STSCLOC (STQCLNO = :myLocation.locationNo)
        STPCORD (STQCODN >= :myOrder.orderDateNo)   // using >= instead of =
      GN STPCORD };

    この手法の欠点は、すべての明示的 I/O の場合と同様です。つまり、階層またはキー・フィールドが変更されても、 明示的 DL/I データベース I/O ステートメントは、自動的に変更されません。

  • それぞれの DL/I セグメント・レコードの定義を変更して 、プログラム・レコード変数の名前で hostVarQualifier プロパティーを組み込みます。 例えば、CustomerRecordPart を以下のように変更します。
    Record CustomerRecordPart type DLISegment 
      { segmentName="STSCCST", keyItem="customerNo",
        hostVarQualifier = "myCustomer" } 
      10 customerNo char(6) { dliFieldName = "STQCCNO" }; //key field 
      ... 
    end
    ここで、以下のレコード宣言および get ステートメントを使用する場合:
    myCustomer CustomerRecodPart;
    myLocation LocationRecordPart;
    myOrder OrderRecordPart;
    myOrderArray OrderRecordPart [] {maxsize = 20};  // オーダーの配列
    get myOrderArray;                  // 最初に配列を埋める
    EGL は、以下の正しい 疑似 DL/I コードを生成します。
    get myOrderArray with #dli{
      GU STSCCST (STQCCNO = :myCustomer.customerNo)
        STSCLOC (STQCLNO = :myLocation.locationNo)
        STPCORD (STQCODN = :myOrder.orderDateNo)
      GN STPCORD };

    この手法の長所は、暗黙的 DL/I データベース I/O を使用できることと、レコード変数および DL/I セグメント・レコードに別の名前を指定することができる点です。 欠点は、CustomerRecordPart、LocationRecordPart、および OrderRecordPart を使用するそれぞれの暗黙的 DL/I データベース I/O ステートメントごとに、修飾子として myCustomer を使用するようになる点です。


フィードバック