2013年4月25日木曜日

For eachの中のローカル式


For eachの中にローカル式を記述する時、For eachのベーステーブルとローカル式のベーステーブル間にリレーションが無い場合は注意が必要です。

例) 国別に顧客数をカウント

1.リレーションがある場合

1-1.データモデル
国マスタと顧客マスタで1対nのリレーションがあるモデルです。

Country(国)トランザクションの定義

Customer(顧客)トランザクションの定義
項目属性名の先頭にあるアイコンが上矢印になっています。
これは外部キー項目である事を表しています。

ダイアグラムに二つのテーブルを配置してみました。
GeneXusは項目属性の名称が同じものは同じ意味合い=同一視し正規化処理を行います。
この例ではCountryId(国ID)がCountry(国)テーブルの主キー、Customer(顧客)テーブルの外部キーとして
存在するので、リレーションが自動的に張られます。
Customerテーブル(物理テーブル)にはCountryName(国名)は存在しません。


1-2.For eachコマンド + ローカル式
For each
    &CountryName = CountryName
    &CustomerCount = Count( CustomerName )
Endfor

For eachコマンドの中にローカル式(ここではCount)を記述しますが、GeneXusがリレーションを認識しているので条件は必要ありません。国1件に対して顧客がn件存在するので、その件数をカウントします。

ビルド時に表示されるナビゲージョンレポート
FormulasセクションでCustomerテーブルに対するCountに
「Given: CountryId」と条件が加わっています。


1-3.生成されるSQL
この場合以下の様なSQLが生成されます。

SELECT T1.[CountryId],
    T1.[CountryName],
    COALESCE( T2.[GXC1], 0) AS GXC1
FROM ([Country] T1 WITH (NOLOCK)
  LEFT JOIN (
    SELECT COUNT(*) AS GXC1, [CountryId]
    FROM [Customer] WITH (NOLOCK)
    GROUP BY [CountryId] ) T2
  ON T2.[CountryId] = T1.[CountryId])
ORDER BY T1.[CountryId]
GeneXusがリレーションを認識しているため、暗黙的にJOINが組み込まれます。


2.リレーションが無い場合

一方、リレーションが無いデータモデルの場合はどうなるでしょうか?

2-1.データモデル

Country(国)トランザクションは変わりませんが、Customer(顧客)トランザクション
はCustomerCountryIdが指定されています。この場合、CountryトランザクションにあるCountryId
とは項目属性名と一致しないので同一視しません。
項目属性名の先頭にあるアイコンが上矢印ではなく・になっています。(外部キーでは無い)

ダイアグラムに二つのテーブルを配置してみました。
CountryテーブルとCustomerテーブルの間にリレーションの線が表示されません。
これはGeneXusがリレーションを認識していないことを表しています。


2-2.For eachコマンド + ローカル式

この場合のローカル式はどう指定すればよいでしょうか? GeneXusはリレーションを認識していないので、Countローカル式に明示的な条件を指定する必要があります。

For each
    &CountryName = CountryName
    &CustomerCount = Count( CustomerName,                           CustomerCountryId = CountryId )
Endfor


ビルド時に表示されるナビゲーションレポート
FormulasセクションではCountの条件が「CustomerCountryId = CountryId」となっています。
注目すべきはCountのアクセス対象。CustomerテーブルとCountryテーブルの二つが挙げられています。
2-3.生成されるSQL

この場合以下の様なSQLが生成されます。

SELECT T1.[CountryId],
T1.[CountryName],
COALESCE( T2.[GXC1], 0) AS GXC1
FROM ([Country] T1 WITH (NOLOCK)
LEFT JOIN (
SELECT COUNT(*) AS GXC1,
T4.[CountryId]
FROM [Customer] T3 WITH (NOLOCK),
[Country] T4 WITH (NOLOCK)
WHERE T3.[CustomerCountryId] = T4.[CountryId]
GROUP BY T4.[CountryId] ) T2
ON T2.[CountryId] = T1.[CountryId])
ORDER BY T1.[CountryId]

オレンジ色の部分がFor eachに該当、青色の部分がCountローカル式に該当します。ローカル式で指定した条件がCount側にJOINとして追加されています。つまりこちらの方が一つ余分にJOINをしている事が判ります。

今回の例はシンプルなモデルですのでパフォーマンスの差は体感できませんが、実際のシステム開発で複雑なモデルや相当量のデータが前提になると目に見える差が発生する場合もあります。

この様に処理結果は同じでも、GeneXusが認識するリレーションの有無で生成されるコードが違う事がありますのでご注意下さい。

そしてローカル式の条件として変数を利用する場合は別な注意が必要です。これはまた次の機会に。



0 件のコメント:

コメントを投稿