2014年4月2日水曜日

データプロバイダーを使いこなそう - 応用編その2 グルーピング集計

今回はデータプロバイダーを使ったグルーピング集計処理です。

考え方としてはプロシージャにおけるグルーピング集計処理と同じになりますが、データプロバイダー言語としての表現がありますので、その点を押さえてください。

まず前提となるデータモデルのおさらいです。前提としては集計の起点となるベーステーブルと集計の対象となるテーブルが1対nのリレーショナルモデルとなります。

データモデルの例。
Customerテーブルを1に対してInvoiceテーブルがnのリレーショナルモデル



データプロバイダーのOUTPUTとなるSDTを作成します。
グルーピング項目と集計項目のみのSDT

1.ローカル式を使ったグルーピング集計

グルーピング集計用データプロバイダーです。ここではローカル式(SUM)を使用しています。
SDT_CustomerInvoice
{
CustomerID = CustomerId
CustomerName = CustomerName
InvoiceAmount = SUM(InvoiceAmount)
}


SUMのパラメータとして条件は指定していません。これはGeneXusの特徴で、データモデル上のリレーションをGeneXusが認識していると、明示的な条件を指定しなくても自動的に生成するコードに条件指定を含めてくれるからです。

今回の例ではCustomerテーブルとInvoiceテーブルの間はCustomerIdによって1対nのリレーションが存在するので、SQLでいう所の「Customer.CustomerID = Invoice.CustomerId」という条件が自動的に組み込まれます。


Webパネルは今までと同じ作り方です。

Refreshイベントでデータプロバイダーを呼び出してSDTコレクションを取得

SDTコレクションをGridとしてWebFormに貼り付ける
実行結果です。

ナビゲーションを見てみましょう。ローカル式 SUMによる集計式が展開されています。この場合、生成されるSQL文は一つになりますので処理効率的にも有利です。


2.入れ子のFor eachを使ったグルーピング集計

ローカル式と同じ結果を得る別な実装方法としては入れ子のFor eachを使います。
データプロバイダーでは直接For eachコマンドは記述できません。{ } で括られたレベルを入れ子に記述します。

SDT_CustomerInvoice
{
CustomerID = CustomerId
CustomerName = CustomerName
&InvoiceAmount = 0
DUMMY
{
&InvoiceAmount = &InvoiceAmount + InvoiceAmount
}
InvoiceAmount = &InvoiceAmount
}


但し、今回のOUTPUTは1レベル構造のSDTですので、内側のレベルは出力する必要がありません。こういったケースでは[NOOUTPUT]オプションを付加することにより、そのレベルがOUTPUT対象からは除外されます。

試しに[NOOUTPUT]オプションを記述せずにビルドすると、OUTPUT構造と一致しないというビルドエラーになります。


ナビゲーションを見てみましょう。

ナビゲーションも入れ子のFor eachと認識しています。この場合はそれぞれSQLが生成され二重のカーソル処理になります。

処理効率という点ではローカル式には劣りますが、ローカル式で指定できない条件を使いたい場合はにはこの入れ子のFor eachを使います。
例えば、Whenを含んだ条件などです。※
   Where InvoiceDate >= &StartDate When not &StartDate.IsEmpty()


3.コントロールブレイクによるグルーピング集計

コントロールブレイクを使ったグルーピング集計の場合、前述したローカル式や入れ子のFor eachとは出力される結果が違ってきます。

SDT_CustomerInvoice
Defined by InvoiceDate
{
CustomerID = CustomerId
CustomerName = CustomerName
&InvoiceAmount = 0
DUMMY [NOOUTPUT]
{
&InvoiceAmount = &InvoiceAmount + InvoiceAmount
}
InvoiceAmount = &InvoiceAmount
}
今回のケースでは、前述の入れ子のFor eachソースに対し外側のFor eachにDefined byオプションを追加しているだけです。これはデータプロバイダーに限らず、For eachコマンドを使ってコントロールブレイクを実装する場合と共通です。(全てのFor eachでベーステーブルを揃える)



ナビゲーションを見てみましょう。

2つのFor eachが同じベーステーブルになつているので、内側のFor eachがBreakとして解釈されています。

実行結果です。違いがわかりますか?


コントロールブレイクを使った場合、他の2つとの違いはInvoiceが0件のCustomerは抽出対象になっていません。つまり「売上データを持った顧客のみを抽出」している事になります。

一方、SUMや入れ子のFor eachを使った場合は、外側=Customer、内側=Invoiceとベーステーブルが違いますので「全顧客に対して売上データを集計して抽出」という事になります。




0 件のコメント:

コメントを投稿