変数を条件に指定するローカル式に注意

前回はFor eachコマンド+ローカル式を使うシーンにて二つのベーステーブル間にリレーションが有る無しで生成結果が違ってくる事を書きました。今回はリレーションが無いデータモデルでの別な注意点についてです。

テーブル間にGeneXusが認識するリレーションが無いデータモデルで、ローカル式の条件に変数を利用する場合として以下の様な記述が考えられます。(基本は明示的に条件を指定する事です)

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

一見問題なさそうに見えますが、実際には国別の顧客数はカウントされず、カウントの結果は全て0件になります。(CountryテーブルにCountryId=0のレコードが無い場合)
何故でしょうか?


ヒントは以前に書きました「ローカル式の挙動に関するあれこれ」にあります。
ケース1.ローカル式の条件に変数を使用している場合
あるコードブロック内にて変数の初期化後にローカル式がある場合、ローカル式が先に評価されるため、条件内の変数が未設定の状態で実行されます。

今回のケースではソース上の記述順が

1.SELECT結果を変数へ代入
    &CountryId = CountryId

2.ローカル式の条件で変数を参照
    &CustomerCount = Count( CustomerName,
CustomerCountryId = &CountryId )

となっていますが、実際にはローカル式の方が先に評価され、変数への代入が後になるので変数値が未定(ここでは&CountryId = 0)の状態でCountが実行されるため、結果値が0件となるのです。

この場合の対処もローカル式をサブルーチン化し、変数への代入とは別ブロック化します。

For each

&CountryId = CountryId
&CountryName = CountryName
Do 'GetCustomerCount'
Endfor

Sub 'GetCustomerCount'
&CustomerCount = Count( CustomerName,
CustomerCountryId = &CountryId )
EndSub
これにより想定と同じ結果が得られるようになります。

但し、今回のケースでは別の観点で注意が必要です。以前の二つのケース(リレーションの有無によるFor eachコマンド内でのローカル式の指定)は結果的に生成されるSQLは一つでした。つまりDBMSに対して一回のクエリーを実行するだけで済みました。

しかし、今回のケース(ローカル式をサブルーチンに分割する方法)に関しては、For eachコマンドで一つのSQL、ローカル式で一つのSQLという二つのSQLに分割され、且つ実行時はFor eachでのCountryレコード取得件数分ローカル式のSQLが実行されることになります。

クエリーの実行回数 = For eachのSQL実行 + ローカル式のSQL実行 x For eachでの取得件数

想像するだけでもパフォーマンスにインパクトがあるのが判りますね。この様に論理的には同じ結果が得られますが、記述によって生成結果・実行パフォーマンスに差が出るケースがあるのです。


コメント

このブログの人気の投稿

データプロバイダーを使いこなそう - 応用編その4 スケジューラー 補足編

HTMLヘッダーにあれこれ直接追加したいとき

GeneXusにおけるWebアプリケーションセキュリティ対策 - OWASP 2010 Top10 Security Risks in GeneXus Applications