< 前へ | 次へ >

演習 3: open および forEach を使用したカーソルの管理

カーソルはブックマークのように動作する SQL 構成体で、行の位置を指定し、一連の検索結果 (つまり、テーブル行) を一度に 1 つずつステップスルーできるようにします。EGL はカーソルを管理できますが、カーソルを利用するための open および forEach ステートメントも提供します。

前回の演習で、delete ステートメントにキーワード noCursor が含まれていたことに気がついたかもしれません。get ステートメントがテーブル全体から 1 行以上を選択するのに対し、通常、delete ステートメントは前のステートメントで作成されたカーソルで示される 1 つの行に作用します。noCursor キーワードは、そのようなカーソルが存在しないことを示し、noCursor ステートメントがデフォルトの get ステートメントと同じ方法で行を指定するために Where 文節を使用することを指定します。

EGL open ステートメントはカーソルを作成し、結果セット、つまり、カーソルが移動可能な 1 行以上のセットを指定します。お客様は、カーソルに直接アクセスする代わりに、他の EGL ステートメントを使用して、カーソルで示されている結果セットから次の行を取得したり、カーソルを使用して一度に 1 行ずつステップスルーしながら、結果セット全体に対する操作を実行します。

カーソルおよび結果セットを EGL で作成するには、open ステートメントを使用して 1 行以上を指定し、新規結果セットに名前を付け、カーソルで示される行を入れる一時変数を提供します。
tempItem Item;
open allExpensiveItems for tempItem;
この例における tempItem は、データベースの ITEMS テーブルを示す、SQLRecord パーツからの単一レコード変数です。コード for tempItem は、EGL がカーソルを使用して一度に 1 行ずつこのレコードに移動することを指定します。コード open allExpensiveItems は、結果セットに名前を付けます。allExpensiveItems は、厳密に言えば、変数ではなく識別子です。
get ステートメントのように、EGL は open ステートメントから SQL SELECT ステートメントを作成するので、お客様はそれを明示して、編集できます。上記の open ステートメントの例で、デフォルトの SELECT ステートメントは次のようになります。
tempItem Item;

open expensiveItems for tempItem with
  #sql{
    select
      EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
      EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
    from EGL.ITEM
    where
      EGL.ITEM.ITEM_ID >= :tempItem.ItemId
    order by
      EGL.ITEM.ITEM_ID asc
  };
このデフォルトの Where 文節で指定しているのは、各項目がその前の項目よりも大きい ITEM_ID 値を持つことのみです。(EGL は SQL DECLARE CURSOR および OPEN CURSOR ステートメントを管理しますが、明示的な SQL では表示しません。)
特定の行を選択して結果セットに追加するには、get ステートメントの場合のように Where 文節を編集します。例えば、次のようにして、所与の変数よりコストが高い項目のみを選択できます。
tempItem Item;
maxCost Price = 500;

open expensiveItems for tempItem with
  #sql{
    select
      EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
      EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
    from EGL.ITEM
    where
      EGL.ITEM.PRICE >= :maxCost
    order by
      EGL.ITEM.ITEM_ID asc
  };
これで、expensiveItems 結果セットに、500 に設定されている maxCost 変数より大きいか等しい PRICE 列を持つデータベースの行がすべて含まれます。
これらの行にアクセスするには、forEach ステートメントを使用します。これは、ループ内のコード・ブロックを実行して、結果セットの各項目にそのコードを適用します。ループを反復するごとに、EGL はカーソルで示された行を一時変数に移動し、カーソルを結果セット内の次の行に進めます。例えば、次のコードは価格が 500 より大きい各項目から値を出力します。
tempItem Item;
maxCost Price = 500;

open expensiveItems for tempItem with
  #sql{
    select
      EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
      EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
    from EGL.ITEM
    where
      EGL.ITEM.PRICE >= :maxCost
    order by
      EGL.ITEM.ITEM_ID asc
  };
  
foreach (tempItem)
  SysLib.writeStdout(tempItem.Name :: " " ::
    tempItem.Description :: " $" :: tempItem.Price);
end
get next を使用すると、結果を手動でステップスルーすることもできます。この場合は、sysVar.sqlData.sqlcode の値をテストするなどして、結果セットにまだ結果があるかどうか調べる必要があります (結果セットにさらに結果がある場合、sysVar.sqlData.sqlcode の値はゼロになります)。
tempItem Item;
maxCost Price = 500;

open expensiveItems for tempItem with
  #sql{
    select
      EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
      EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
    from EGL.ITEM
    where
      EGL.ITEM.PRICE >= :maxCost
    order by
      EGL.ITEM.ITEM_ID asc
  };

// Retrieve the first row.
get next tempItem;

// As long as there are more rows,
// move the next row into the temporary variable
// and print its values. 
while (sysvar.sqlData.sqlcode == 0) 
  SysLib.writeStdout(tempItem.Name :: " " ::
    tempItem.Description :: " $" :: tempItem.Price);
  get next tempItem;
end
get ステートメントには、オープン・カーソルおよび結果セットで使用可能な様々な操作が他にもあります。
  • get next を使用して、前の例のように一時変数を指定するのではなく、結果セットを指定できます。例えば、get next from expensiveItems は、結果セットの次の行を一時変数に移動します。このコードは get next tempItem と同等です。 同様に、forEach ステートメントで結果セット名を使用できます。forEach (from expensiveItems)forEach (tempItem) と同等です。
  • get current は、カーソルが現在示している行を、カーソルを次の行に移動することなく取得します。
  • get first および get last は、結果の先頭と末尾の行をそれぞれ取得します。
  • get previousget next の逆で、カーソルの前の行を取得します。
  • get absolute(position) は、結果セット内の特定の位置にある行を取得します。例えば、get absolute(5) tempItem は結果セット内の 5 行目を取得し、get absolute(-2) tempItem は最後から 2 行目を取得します。
  • get relative(position) は、カーソルの現在位置に対して特定の相対位置にある行を取得します。例えば、get relative(3) tempItem は、カーソルの現在位置から 3 行後の行を取得し、get relative(-2) tempItem はカーソルの現在位置の 2 行前の行を取得します。get relative(0) tempItemget current tempItem と同等です。
get next 以外のこれらの定位置 get ステートメントを使用するには、結果セットが scrollable であることを指定するか、先頭から末尾まで一度に 1 つの行をステップスルーするのではなく、カーソルの位置を手動で変更する意図があることを示す必要があります。次の例で示すように、scroll キーワードを open ステートメントに追加します。
open expensiveItems scroll for tempItem;
EGL は、いくつかある条件のいずれかに合致すると、カーソルを自動的に閉じます。例えば、カーソルが結果セットの末尾にある、同じ結果セット上の別の open ステートメントにある、あるいは、プログラムの終わりにあるという理由で、get next ステートメントで結果が戻らない場合などです。カーソルおよび結果セットの操作が終了した場合、次の例のように、EGL close ステートメントを使用して閉じることができます。
close expensiveItems;
カーソルと結果セットが閉じられると、カーソルに依存していた get next やその他のステートメントは機能しなくなります。

結果セットのステップスルー

この演習では、forEach ステートメントを使用して結果セットをステップスルーし、データベースの項目のうち、ある価格を上回るものを取得します。

  1. programs パッケージ内に、selectItems という名前の新規プログラムを作成します。
  2. プログラムからデフォルトのコードを除去して、プログラムを次のようにします。
    package programs;
    
    program selectItems type BasicProgram {}
      
      function main()
      end
      
    end
  3. データベースへのアクセス中に EGL で発生する可能性のあるすべてのエラーを処理するために、次の関数を追加します。
    function handleDBException(exception SQLException)
      SysLib.writeStdout("Error accessing database. "::
        "See Troubleshooting section");
      SysLib.writeStdout(exception.message);
    end
    データベース・アクセスで発生したエラーの処理については、この後のチュートリアルで学習します。
  4. 次の関数を追加して、最大コストの Price パラメーターに基づいてデータベースから行を除去します。
    function printExpensiveItems(maxPrice Price in)
      
    end
    Price dataItem パーツは、DECIMAL プリミティブ型のものです。 このパーツは、ファイル eglderbyr7/primitivetypes/data に定義されています。
  5. 関数宣言で Price 変数を追加する際にコンテンツ・アシストを使用しなかった場合は、次の import ステートメントをファイル上部の package ステートメントの下に追加します。
    import eglderbyr7.primitivetypes.data.*;

    コンテンツ・アシストを使用すると、キーワードおよびタイプの名前を、最初の数文字を入力し、Ctrl + スペースを押すことで入力できることを覚えておいてください。コンテンツ・アシストを使用して、Price dataItem パーツなどのタイプを挿入すると、コンテンツ・アシストにより、適切な import ステートメントがファイルに追加されます。

  6. 新規関数内に、カーソルおよび結果セットを作成するための open ステートメントを追加します。カーソルで示される行を保持する一時 Item 変数も必要になります。
      tempItem Item;
      open expensiveItems for tempItem;
  7. この場合も、Item レコード・タイプを挿入する際にコンテンツ・アシストを使用しなかった場合は、次の import ステートメントを追加して Record パーツをスコープに入れます。
    import eglderbyr7.data.Item;
  8. open ステートメント上にカーソルを置いて右クリックし、「SQL ステートメント」 > 「追加」の順にクリックして、SQL コードを明示します。 open ステートメントは次のようになります。
      open expensiveItems for tempItem with
        #sql{
          select
            EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
            EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
          from EGL.ITEM
          where
            EGL.ITEM.ITEM_ID >= :tempItem.ItemId
          order by
            EGL.ITEM.ITEM_ID asc
        };
  9. 明示的な SQL コードの Where 文節を変更して、関数に渡される maxPrice 変数より大きいか等しい EGL.ITEM.PRICE 列の値を持つ行のみを選択するようにします。
      open expensiveItems for tempItem with
        #sql{
          select
            EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
            EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
          from EGL.ITEM
          where
            EGL.ITEM.PRICE >= :maxPrice
          order by
            EGL.ITEM.ITEM_ID asc
        };
    maxPrice 変数 が SQL のキーワードや値ではなく、EGL 変数であることを示すために、maxPrice 変数の前にコロンを入れることを忘れないでください。
  10. open ステートメントの後に、項目についての情報をコンソールに出力する forEach ループを追加します。
      forEach (tempItem)
        SysLib.writeStdout(tempItem.Name :: " " ::
          tempItem.Description :: " $" :: tempItem.Price);
      end
    完成した関数は次のようになります。
    function printExpensiveItems(maxPrice Price in)
      
    tempItem Item;
    open expensiveItems for tempItem with
      #sql{
        select
          EGL.ITEM.ITEM_ID, EGL.ITEM.NAME, EGL.ITEM.IMAGE, 
          EGL.ITEM.PRICE, EGL.ITEM.DESCRIPTION
        from EGL.ITEM
        where
          EGL.ITEM.PRICE >= :maxPrice
        order by
          EGL.ITEM.ITEM_ID asc
      };
      
    forEach (tempItem)
      SysLib.writeStdout(tempItem.Name :: " " ::
        tempItem.Description :: " $" :: tempItem.Price);
    end
  11. main 関数から関数を呼び出し、maxPrice の値を渡します。
    function main()
      try
        printExpensiveItems(200);
      onException(exception SQLException)
        handleDBException(exception);
      end
    end
  12. プログラムを生成し、実行します。
プログラムにより、データベース内の売上項目のうち、関数に渡された値と等しいかそれより大きい価格の項目名が出力されます。例えば、200 を関数に渡すと、次のような結果になります。
Laptop PC Great little machine.  Take it anywhere with you. $1111.11
Combination Safe Keep your valuables safe and secure.  Put 'em in here. $222.22
Desktop PC Lots'a power, at the right price.  Includes monitor. $888.55

これで、selectItems.egl ファイルのコードが完成しました。 このファイル内にエラーがある場合 (赤の X 記号でマークされている場合) は、演習 3 で完成した selectItems.egl ファイルのファイルに記載されているコードと作成したコードが一致していることを確認してください。

演習のチェックポイント

この演習では、結果セットの 1 行を open および forEach を使用して一度に処理する方法を学習しました。

結果のリスト全体をレコードの配列に取り出す際に、カーソルを使用して結果セットの 1 行を一度に処理すると、get を使用するよりも便利で効率的です。
詳しくは、open および forEach についてのヘルプ・トピックを参照してください。
< 前へ | 次へ >