あるテーブル内のすべてのレコードに対して処理を行う際に、そのレコードに関連するレコードが
別のテーブルに存在する場合があります。この場合、2 つ目のテーブルに格納されているデータを一定の基準でグループ化し、スキャンして処理する必要があります。これはコントロールブレークとして知られています。
次の
トランザクション構造を見てみます:
Country
{
CountryId*
CountryName
}
Customer
{
CustomerId*
CustomerName
CustomerEmail
CountryId
CountryName
}
すべての顧客を国別にグループ化してリストにするよう指示されたとします。この場合、顧客が存在する国のみをリストに含める必要があります。
COUNTRY テーブルから国を取得するのは適切ではありません。このテーブルには、顧客がいるかどうかに関係なく、多数の国が入力されているからです。一方、CUSTOMER テーブルでは、各顧客の国を抽出する必要があります。そのためには、顧客のいる国のみを取得します。顧客がいない国を除外し、同じ国の顧客を
グループ化して、それぞれの国名に続いて関連する顧客のデータを出力する必要があります。
コントロールブレークの定義は簡単です。次の手順で行います:
1.
ネストされた For Each コマンドを使用します。
2.
ベーステーブルが同じである必要があります。
2.1. For Each コマンドの数は、ブレークの数よりも 1 つ多くなります。
3. 外側の
For Each コマンドの
Order 節で、「ブレークする」項目属性を 1 つまたは複数設定する必要があります。
次のプロシージャーには、これらの要件を満たすコントロールブレークが定義されています。
For each Customer order CountryName
print country {CountryName}
For each Customer
print customer {CustomerName, CustomerEmail}
Endfor
Endfor
上のコードより、GeneXus は、ベーステーブルが CUSTOMER であり、CUSTOMER テーブルの最初のレコードの国名をメモリーに維持する必要があると判断します (order 節の横で指定している項目属性が CountryName であるため)。次に、国名を出力し (外側の For Each 内の最初の文)、国名が変わらないかぎり、ネストされた For Each を実行し、その国名を持つすべてのレコードを処理します。レコードの国名が変わった場合は、外側の For Each をもう一度実行し、同じ処理を繰り返します。
ネストされた For Each コマンドが 2 つある場合でも、テーブルへのアクセスが 2 回になるわけではありません。ポインターがテーブルに沿って順番に移動する様子をイメージしてください。テーブルはグループ化基準に従ってアクセスされるため、レコードは順番に処理され、各国名が一度表示されてから、その国の顧客データが続くと想定できます。
コントロールブレークの「手順」に従ったコードで、GeneXus は次の処理を行います:
1. 外側の For Each の Order 節で指定されている項目属性 (この例では CUSTOMER の
拡張テーブルに含まれる CountryName) の順序でベーステーブル (この例では CUSTOMER) をスキャンします。
2. ポインター位置のレコードごとに、ネストされた For Each の前に定義されている文を実行します (この例では、CountryName 項目属性の値を取得し、出力しています)。
3. Order 節で指定されている項目属性 (この例では CountryName) の値が変化するまでの間に、次の処理が行われます。
3.1. ネストされた For Each 内で定義されている文を実行します (この例では、ポインター位置のレコードの CustomerName と CustomerEmail の値が同じ行に出力されます)。
3.2. 次のレコードを読み取ります。
4. テーブルの末尾に到達するか、CountryName の値が変わった場合は、手順 2 に戻ります。
次の図では、メインの For Each で処理されるレコードと、ネストされた For Each で処理されるレコードを示すため、顧客データと国の関係を示しています。

実行時のリストは次のようになります:

COUNTRY テーブルには、関連する顧客がいないためリストに含まれない国がほかにも多数格納されています。
CountryName でグループ化してから、各国の顧客を CustomerName で並べ替えて出力する場合は、次のようにネストされた For Each に、必要な Order 節を追加するだけです (この例では、order CustomerName)。
For each Customer order CountryName
print country {CountryName}
For each Customer order CustomerName
print customer {CustomerName, CustomerEmail}
Endfor
Endfor
GeneXus は、各 For Each コマンドの Order 節の連結で作成されたインデックスの順序で CUSTOMER テーブルをスキャンします (上の例では: CountryName、CustomerName)。したがって、この複合インデックスの順序でレコードがスキャンされ、CountryName (外側の For Each の Order 節で指定されている項目属性) ごとにブレークが実行され、関連する顧客が国ごとに CustomerName 順に表示されます (レコードは CountryName、CustomerName の順序でアクセスされます)。
ここで、次のトランザクション構造を考えます (前述のものとほぼ同じですが、Continent トランザクションが新たに加わり、各国がいずれか 1 つの大陸に属します)。
Continent
{
ContinentId*
ContinentName
}
Country
{
CountryId*
CountryName
ContinentId
ContinentName
}
Customer
{
CustomerId*
CustomerName
CustomerEmail
CountryId
CountryName
}
顧客を大陸別、国別にグループ化して示すリストが必要であるとします。このリストには、顧客が登録されている大陸と国のみを含めます。

大陸別のグループ化が必要になり、このグループ内で国別にグループ化するため、同じベーステーブルに対して、ネストされた For Each コマンドが 3 つ必要になります (リストには、顧客が関連付けられている大陸と国のみを含めるため、CUSTOMER テーブルがベーステーブルである必要があります)。ブレーク基準を設定するには、Order 節が不可欠です。この場合、ContinentName でグループ化してから、この大陸内のすべての顧客を CountryName でグループ化する必要があります。最初の For Each は ContinentName の順序、直下のネストされた For Each コマンドは CountryName の順序になります。
For each Customer order ContinentName
print continent {ContinentName}
For each Customer order CountryName
print country {CountryName}
For each Customer
print customer {CustomerName, CustomerEmail}
Endfor
Endfor
Endfor
上のコードで、GeneXus は、各 For Each コマンドの Order 節の連結で作成されたインデックスの順序で CUSTOMER テーブルをスキャンします (ContinentName、CountryName)。次に、CUSTOMER テーブルの最初のレコードを取得し、大陸名をメモリーに維持します (最初のブレーク基準が ContinentName であるため)。その後、大陸名を出力します (外側の For Each 内の最初の文)。大陸名が変わらないかぎり、ネストされた For Each を実行して、その大陸名のレコードをすべて順番に処理します。国名はメモリー内に維持されます (2 番目のブレーク基準が CountryName であるため)。次に、国名を出力します (2 番目の For Each 内の最初の文)。その後、
大陸名と国名が変わらないかぎり、最後のネストされた For Each を実行し、
その大陸名と国名のすべてのレコードを順番に処理します。レコードの国名が変わった場合は、そのすぐ外側にある For Each と、その中の文 (最後のネストされた For Each) を実行します。レコードの大陸名が変わった場合は、外側の For Each を実行し、テーブルの末尾に到達するまで同様に処理を進めます。
How to process grouped information