2013年8月5日月曜日

GeneXusでのサブクエリ(副問い合わせ)の実装方法

前回のエントリーでGeneXusにおけるJOINの話をしましたが、今回はGeneXusでのサブクエリーの実装方法です。

以下の様なデータモデルで、Invoice一覧の画面を実装する時に、検索条件としてInvoiceDetailつまり明細の項目を含めたい場合はどう実装したらよいでしょうか?





例えばfor eachコマンドで記述してみます。



for each
  //ヘッダーの項目属性
  where InvoiceDate = &InvoiceDate when not &InvoiceDate.IsEmpty()
  where CustomerName like &CustomerName when not &CustomerName.IsEmpty()
  //明細の項目属性
  where ProductDescription like &ProductDescription when not &ProductDescription.IsEmpty()

  //表示項目は全てヘッダーの項目属性
  &InvoiceId = InvoiceId
  &InvoiceDate = InvoiceDate
  &CustomerName = CustomerName
  &InvoiceAmount = InvoiceAmount

  Load
when none
  // not found時の処理
endfor

いかがでしょうか? 皆さんの考えた通りの記述でしたか?
実はこの記述は間違いです。想定する動作になりません。


このfor eachコマンドをGeneXusが解析すると「ベーステーブル=InvoiceDetail、拡張テーブル=InvoiceDetail、Invoice、Customer」となります。



InvoiceDetailがベーステーブルになるので明細が複数あるInvoiceレコードに関しては同じデータが明細数分一覧表示される事になります。拡張テーブルはSQLではINNER JOINになります。


実際の挙動。明細の件数分同じヘッダーデータが抽出されます。又、明細が無いヘッダーは抽出されません。



GeneXusがベーステーブルを特定する時のポイントは「for eachコマンド内に記述された項目属性」つまりOrder節Where節に記述された項目属性も含みます。
今回の例では、Where節にProductDescriptionを加えてしまった時点で、Invoiceがベーステーブルになる事はありません。


では、今回の様な要件を実装したい場合はどうしたらよいでしょうか?
一般的(SQLでは)にはサブクエリ(副問い合わせ)を使用しますが、GeneXusではデータセレクタ※を使う事により実装が可能になります。

※データセレクタとは、Order/Where/Defined by といったfor eachコマンドに対するオプションを部品化し、複数の箇所(for eachコマンド/Grid/式など)で再利用する事を目的としたオブジェクトです。


まず、データモデルで説明すると、for eachのベーステーブル・拡張テーブルとサブクエリ側=データセレクタのベーステーブル・拡張テーブルが以下の様になります。




以下が実際の定義です。
・データセレクタの作成

Parameter データセレクターのパラメータ定義
parmルールと同じ
Order for eachコマンドのOrder節定義
Condition for eachコマンドのWhere節定義
Defined byfor eachコマンドのDefined by定義


・for eachコマンドの記述
for each
  //ヘッダーの項目属性
  where InvoiceDate = &InvoiceDate when not &InvoiceDate.IsEmpty()
  where CustomerName like &CustomerName when not &CustomerName.IsEmpty()
  //データセレクターを使った副問い合わせ
  where InvoiceId in InvoiceDetailDataSelector( &ProductName ) when not &ProductName.IsEmpty()

  //表示項目は全てヘッダーの項目属性
  &InvoiceId = InvoiceId
  &InvoiceDate = InvoiceDate
  &CustomerName = CustomerName
  &InvoiceAmount = InvoiceAmount

  Load
when none
  // not found時の処理
endfor

GeneXusの解析結果でベーステーブルがInvoice、拡張テーブルがInvoice、Customerになっています。


実際の挙動。複数明細持っているヘッダーデータも1件しか抽出されません。又、明細が無いヘッダーデータは抽出されるようになりました。





0 件のコメント:

コメントを投稿