7.26.4 DEF_LIST の使用例

例1:注文の明細をすべて表示するRDMLプログラムを作成します。

必要な明細行リストを名前#ORDERLINEで定義し、注文頭書きファイルの必要なフィールドを名前#ORDERHEADでグループ化します。

DEF_LIST   NAME(#ORDERLINE) FIELDS(#ORDLIN #PRODUCT #QUANTITY #PRICE)
GROUP_BY   NAME(#ORDERHEAD) FIELDS(#ORDNUM #CUSTNUM #DATEDUE)
 

ユーザーに注文番号を入力するよう要求し、リスト内のすべての項目をクリアして表示モードに設定します。

L1: REQUEST    FIELDS(#ORDNUM)
    SET_MODE   TO(*DISPLAY)
    CLR_LIST   NAMED(#ORDERLINE)
 

必要なフィールドをORDHDRファイルから取得します。見つからなかった場合は、自動エラー・メッセージと共にREQUESTコマンドに戻ります。

FETCH      FIELDS(#ORDERHEAD) FROM_FILE(ORDHDR)  WITH_KEY(#ORDNUM) NOT_FOUND(L1)  ISSUE_MSG(*YES)
 

ORDLINファイルから必要なフィールドを選択します。選択したレコードごとに、新しい項目を#ORDERLINEという名前のリストに追加します。

SELECT     FIELDS(#ORDERLINE) FROM_FILE(ORDLIN) WITH_KEY(#ORDNUM)
ADD_ENTRY  TO_LIST(#ORDERLINE)
ENDSELECT
 

最後に、注文のヘッダー・フィールドと明細行の詳細をユーザーに表示します。

DISPLAY    FIELDS(#ORDERHEAD) BROWSELIST(#ORDERLINE)
 

LANSAがこのRDMLプログラム用に自動的に設計する画面形式は以下のようになります。

REQUEST FIELDS(#ORDNUM)コマンドの場合:

         注文番号:______________

DISPLAY FIELDS(#ORDERHEAD) BROWSELIST(#ORDERLINE)コマンドの場合:

         注文番号:99999999                           

         顧客番号:999999                             

         期限:99/99/99                           

                                                           

         行                                              

         番号   製品 数量 価格                       

         99   9999999  99999  99999.99                     

         99   9999999  99999  99999.99                     

         99   9999999  99999  99999.99                     

         99   9999999  99999  99999.99                     

         99   9999999  99999  99999.99                     

         99   9999999  99999  99999.99                     

                                                           

例2:上記の例で使用したRDMプログラムを変更し、PROMST (製品マスター)ファイルのフィールド#PDESC (製品の説明)をリストに含めます。

リスト定義に#PDESCを含めます。

DEF_LIST  NAME(#ORDERLINE) FIELDS(#ORDLIN #PRODUCT #PDESC #QUANTITY #PRICE)
GROUP_BY  NAME(#ORDERHEAD) FIELDS(#ORDNUM #CUSTNUM #DATEDUE)
 
L1: REQUEST    FIELDS(#ORDNUM)
    SET_MODE   TO(*DISPLAY)
    CLR_LIST   NAMED(#ORDERLINE)
    FETCH      FIELDS(#ORDERHEAD) FROM_FILE(ORDHDR) WITH_KEY(#ORDNUM) NOT_FOUND(L1) ISSUE_MSG(*YES)
 

ORDLINファイルから必要なフィールドを選択します。選択したレコードごとに、関連付けられた製品の説明を取得し、新しい項目を#ORDERLINEというリストに追加します。

SELECT     FIELDS(#ORDERLINE) FROM_FILE(ORDLIN) WITH_KEY(#ORDNUM)
FETCH      FIELDS(#PDESC) FROM_FILE(PROMST) WITH_KEY(#PRODUCT)
ADD_ENTRY  TO_LIST(#ORDERLINE)
ENDSELECT
 
DISPLAY    FIELDS(#ORDERHEAD) BROWSELIST(#ORDERLINE)
 

LANSAがこの変更後のRDMLプログラム用に自動的に設計する画面形式は以下のようになります。

REQUEST FIELDS(#ORDNUM)コマンドの場合:

         注文番号: ___________________

DISPLAY FIELDS(#ORDERHEAD) BROWSELIST(#ORDERLINE)コマンドの場合:

        

  注文番号:99999999                           

  顧客番号:999999                             

  期限     :99/99/99                           

                                                           

  行                                              

  番号   製品の説明        数量 価格    

  99   9999999 XXXXXXXXXXXXXXXXXXXX 99999  99999.99 

  99   9999999 XXXXXXXXXXXXXXXXXXXX 99999  99999.99 

  99   9999999 XXXXXXXXXXXXXXXXXXXX 99999  99999.99 

  99   9999999 XXXXXXXXXXXXXXXXXXXX 99999  99999.99 

  99   9999999 XXXXXXXXXXXXXXXXXXXX 99999  99999.99 

  99   9999999 XXXXXXXXXXXXXXXXXXXX 99999  99999.99 

                                                           

例3:姓(すべてまたは一部)を入力するようユーザーに要求し、指定された値で名前が始まるすべての社員のリストを表示する、以下の単純なRDMLプログラムについて考えます。

********   Define work variables and browse list to be used
DEFINE     FIELD(#L1COUNT) TYPE(*DEC) LENGTH(7) DECIMALS(0)
DEF_LIST   NAME(#L1) FIELDS((#SURNAME) (#GIVENAME) (#EMPNO) (#ADDRESS1)) COUNTER(#L1COUNT)
********   Loop until terminated by EXIT or CANCEL
BEGIN_LOOP
********   Get surname to search for
REQUEST    FIELDS(#SURNAME)
********   Build list of generically identical names
CLR_LIST   NAMED(#L1)
SELECT     FIELDS(#L1) FROM_FILE(PSLMST2) WITH_KEY(#SURNAME) GENERIC(*YES)
ADD_ENTRY  TO_LIST(#L1)
ENDSELECT
********   If names found, display list to user
IF         COND('#L1COUNT *GT 0')
DISPLAY    BROWSELIST(#L1)
********   else issue error indicating none found
ELSE
MESSAGE    MSGTXT('No employees have a surname matching request')
ENDIF
********   Loop back and request next name to search for
END_LOOP
 

機能的にはこれで充分ですが、例えば検索キーとして「D」を指定し、これに合致する従業員が800人いたとすればどうなるでしょうか。

この場合、800行ものリストが表示されることになってしまい、ユーザーにとって見にくい上に、相当の時間とコンピュータ資源を要します。

これを解消するため、所定の行数ごとにページで区切って表示する方法がよく使われます。すなわち、条件に合致するレコードを「1ページ分」だけ検索し、これを表示するという方法です。次のページに進むためのキーが押されれば、改めてレコードを検索し、表示することになります。

このプログラムに「ページ単位」技法を実装するには、以下のように変更します。

********   Define work variables and browse list to be used
DEFINE     FIELD(#L1COUNT) TYPE(*DEC) LENGTH(7) DECIMALS(0)
DEFINE     FIELD(#L1PAGE) TYPE(*DEC) LENGTH(7) DECIMALS(0)
DEFINE     FIELD(#L1TOP) TYPE(*DEC) LENGTH(7) DECIMALS(0)
DEFINE     FIELD(#L1POS) TYPE(*CHAR) LENGTH(7)
DEF_LIST   NAME(#L1) FIELDS((#SURNAME) (#GIVENAME) (#EMPNO) (#ADDRESS1)) COUNTER(#L1COUNT) PAGE_SIZE(#L1PAGE) TOP_ENTRY(#L1TOP) SCROLL_TXT(#L1POS)
********   Loop until teminated by EXIT or CANCEL
BEGIN_LOOP
********   Get surname to search for
REQUEST    FIELDS(#SURNAME)
********   Build list of generically identical names
CLR_LIST   NAMED(#L1)
CHANGE     FIELD(#IO$KEY) TO(UP)
CHANGE     FIELD(#L1TOP) TO(1) 
SELECT     FIELDS(#L1) FROM_FILE(PSLMST2) WITH_KEY(#SURNAME) GENERIC(*YES) WHERE('#IO$KEY = UP') OPTIONS(*ENDWHERE) 
EXECUTE    SUBROUTINE(DISPLAY) WITH_PARMS('''More...'''
ADD_ENTRY  TO_LIST(#L1)
ENDSELECT
 ********   If names found, display list to user
IF         COND('#L1COUNT *GT 0')
EXECUTE    SUBROUTINE(DISPLAY) WITH_PARMS('''Bottom''')
********   else issue error indicating none found
ELSE
MESSAGE    MSGTXT('No employees have a surname matching request')
ENDIF
********   Loop back and request next name to search for
END_LOOP
********
********   Display names if page is full or list is complete
********
SUBROUTINE NAME(DISPLAY) PARMS(#L1POS) 
DEFINE     FIELD(#L1REMN) TYPE(*DEC) LENGTH(5) DECIMALS(5)
CHANGE     FIELD(#L1REMN) TO('#L1COUNT / #L1PAGE'
IF         COND('(#L1COUNT *NE 0) *AND (#IO$KEY = UP) *AND ((#L1POS = ''Bottom'') *OR (#L1REMN *EQ 0.00000))')
DOUNTIL    COND('(#L1POS *NE ''Bottom'') *OR (#IO$KEY *NE UP)'
DISPLAY    BROWSELIST(#L1) USER_KEYS((*ROLLUP)) 
ENDUNTIL 
CHANGE     FIELD(#L1TOP) TO('#L1TOP + #L1PAGE'
ENDIF 
ENDROUTINE 
 

この手法は、表示するリストの行数が膨大になりうる状況で一般的に使えるもので、処理性能の向上にも効果があります。

「最初の例」に挙げたような、SELECTとDISPLAYを組み合わせて実装したプログラムは、この手法を取り入れて比較的簡単に最適化できます。プログラム構成や処理の流れにはあまり手を入れず、局所的な修正で対処できることがわかるでしょう。

この手法を実際に組み込むためには、サイトの要求に合致した「標準」アルゴリズムを設計し、充分にテストしておくとよいでしょう。これをテンプレートとして個々のアプリケーションに適用することができます。

例4:TRANSという取引ファイルを印刷します。このファイルには、10,000個のレコードが保持されています。印刷する取引ごとに、それに関連付けられた州の記述をファイルSTATESから抽出して印刷する必要があります。

この操作を実行する単純なRDMLプログラムは以下のようになります。

GROUP_BY   NAME(#TRANS) FIELDS(#TRANNUM #TRANTIME #TRANDATE #TRANTYPE #TRANUSER #TRANSTATE #STATEDES)
 
SELECT     FIELDS(#TRANS) FROM_FILE(TRANS)
FETCH      FIELDS(#TRANS) FROM_FILE(STATES) WITH_KEY(#TRANSTATE)
UPRINT     FIELDS(#TRANS)
ENDSELECT
 

ただし、作業リストを使用すれば、このプログラムを大幅に高速化できます。

GROUP_BY   NAME(#TRANS) FIELDS(#TRANNUM #TRANTIME #TRANDATE #TRANTYPE #TRANUSER #TRANSTATE #STATEDES)
DEF_LIST   NAME(#STATES) FIELDS(#STATE #STATEDES) TYPE(*WORKING) ENTRYS(10)
 
SELECT     FIELDS(#STATES) FROM_FILE(STATE)
ADD_ENTRY  TO_LIST(#STATES)
ENDSELECT
 
SELECT     FIELDS(#TRANS) FROM_FILE(TRANS)
LOC_ENTRY  IN_LIST(#STATES) WHERE('#STATE = #TRANSTATE')
UPRINT     FIELDS(#TRANS)
ENDSELECT
 

州が10個ある場合、この印刷プログラムでは、最初のバージョンよりもデータベース・アクセスが9,990回少なくなります。

以下のように、FETCHコマンドのKEEP_LASTパラメータを使用することにより、さらに簡単な方法でまったく同じようにパフォーマンスを高めることができます。

GROUP_BY   NAME(#TRANS) FIELDS(#TRANNUM #TRANTIME #TRANDATE #TRANTYPE #TRANUSER #TRANSTATE #STATEDES)
 
SELECT     FIELDS(#TRANS) FROM_FILE(TRANS)
FETCH      FIELDS(#TRANS) FROM_FILE(STATES) WITH_KEY(#TRANSTATE) KEEP_LAST(10)
UPRINT     FIELDS(#TRANS)
ENDSELECT