FOREACH関数

FOREACH関数では、セットの各メンバーに対して計算を行い、その結果をセットにします。

FOREACHは、LETSELECT、行関数または集計関数の引数、WHEREHAVINGORDER BYなどの式を受け付けるEQL文内のコンテキストで使用できます。FOREACHは常にセットに評価されるため、コンテキストはセット型の式を受け付ける必要があります。そうではない場合、EQLはチェック・エラーを表示します。

構文

FOREACH関数の構文は次のとおりです。
FOREACH id_1 IN set_1[, id_n IN set_n] RETURN(expression)
ここで、
  • idは、計算対象の項目の任意の識別子です。識別子では、NCName書式を使用する必要があります。
  • setはセット・データ型の1つのセットです。
  • expressionはEQL式です。式はカッコで囲む必要があり、RETURNキーワードが必要です。使用されるidパラメータの数に関係なく、関数に必要なRETURNは1つのみです。

少なくとも1つの識別子とセットのペア(id IN set)およびRETURN式を指定する必要があります。

スコープおよびシャドーイング

FOREACHRETURN式内のid_1からid_nまでの識別子をバインドします。識別子はset_1からset_nまでのユニバースのスコープにはありません。これらのバインドによって、FOREACH関数の時点でスコープにあるid_1からid_nまでの他のバインドはシャドーイングされます。

EVERYおよびSOME数量詞同様、EQLではこれらのバインドされた変数への参照をデータソース別名で修飾できません。たとえば、次の文があるとします。
RETURN results AS
  SELECT
    Source.x AS x,
    FOREACH x IN {1, 2}, y in {3, 4} RETURN (results.x + Source.y) AS vals
  FROM Source
EQLはresults.xへの参照(FOREACH RETURN式内)をSELECT句によって定義されるxへの参照(つまり、FOREACHによってバインドされたxへの参照ではなく、Source.xの別名)と解釈します。同様に、EQLは参照Source.ySourceの属性yの参照と解釈します。
ただし、文修飾子を削除すると次のようになります。
RETURN results AS
  SELECT
    Source.x AS x,
    FOREACH x IN {1, 2}, y in {3, 4} RETURN (x + y) AS vals
  FROM Source
xおよびyは、これより前のSELECTおよびデータソースのスコープ内にすでにありますが、EQLはxおよびy (FOREACH RETURN式内)をFOREACHでバインドされた識別子と解釈します。したがって、valsは常に値{4, 5, 6}になります。

FOREACHの構文では、set_1からset_nはセット・データ型(mdex:string-setなど)である必要があります。そうではない場合、EQLはエラーを表示します。対応するx_1からx_nまでの識別子は、これらのセットの要素の型である必要があります。説明のため、次の例について考えてみます。
FOREACH x IN {1, 2}, y IN {'abc', 'def'} RETURN (x + y)
最初のユニバースは整数のセットであるため、xRETURN式内で整数型です。同様に、2番目のユニバースは文字列のセットであるため、yは文字列型です。その結果、RETURNx + yは、不正な型になります(EQLはその旨のエラーを表示します)。

RETURN式にアトミック型tがある場合、FOREACH式全体が"tのセット"型になります。したがって、RETURN式の型が整数の場合、これを含むFOREACH式は整数セット型です。

一方、RETURN式がセット型の場合、FOREACH式はRETURN式と同じ型になります。(これは、FOREACHRETURN式の値の共用体をとる場合に相当します。)このため、RETURN式が文字列のセットを生成する場合、FOREACH式も文字列をとります。

FOREACHと集計

FOREACH式は、事前グルーピングおよび事後グルーピングの両方の計算(WHEREHAVINGおよびORDER BY句)で使用できます。数量詞式(EVERYおよびSOME)とほぼ同様に集計と対話します。
  • FOREACH式は、集計関数の引数内で使用できます。
    RETURN results AS
      SELECT
        SET_UNIONS(FOREACH x IN e RETURN(b)) AS unions
      FROM Source
      GROUP
  • 集計関数は、FOREACHとそのバインドされた変数の間で使用できません。つまり、次は無効で、EQLはその旨のエラーを表示します。
    FOREACH x IN e RETURN(SUM(x))

操作の詳細

FOREACHの動作を説明するために、簡単な例から始めます。
FOREACH x IN {1, 2, 3} RETURN(x * 2 + 1)

この式は、セット{3, 5, 7}と評価されます。概念上EQLはRETURNx * 2 + 1をセット{1, 2, 3}のメンバーごとに1回評価する(xはセットの各要素を順にとる)ためです。最後にEQLはこれらの評価の結果を別のセットにまとめます。

ユニバースと結果が両方ともセットであるため、ユニバースの要素のトラバースの順序や結果の値の最終セット内の出現順序は指定できません。

FOREACHを使用して複数セットの反復も可能です。
FOREACH x IN {1, 2, 3}, y IN {40, 50, 60} RETURN(x + y)
この式は、セット{41, 42, 43, 51, 52, 53, 61, 62, 63}と評価されます。ここでEQLは、値xyの2つのセットからのすべての可能な組合せについてRETURNx + yを評価します。
x = 1, y = 40: x + y = 41
x = 2, y = 40: x + y = 42
x = 3, y = 40: x + y = 43
x = 1, y = 50: x + y = 51
...
x = 3, y = 60: x + y = 63
セットには重複する値やNULLを含むことはできないため、次の2つの例で示すようにFOREACH式の結果がユニバースより小さくなる(ユニバースのクロス積になる)ことがあります。
FOREACH x IN {-1, 1, 2}      RETURN (ABS(x))        // returns {1, 2}
FOREACH x IN {'3', '4', 'y'} RETURN (TO_STRING(x)) // returns {3, 4}
最初の例では、ABS(-1) = ABS(1) = 1のため、最終セットには2つの要素のみが含まれ、値1は2回出現しません。2番目の例では、TO_INTEGER('x')はNULLのため、この値は最終セットに出現しません。
RETURN式自体がセット(1つの値ではなく)の場合、FOREACHは前述のように評価しますが、すべてのRETURNのすべてのセットの共用体を最終セットとして計算します。次に例を示します。
FOREACH x in {3, 4, 5}, y IN {-1, 0, 2} RETURN ({x + y, x - y})
次のように、セット{1, 2, 3, 4, 5, 6, 7}と評価されます。
// Note: "body" is the RETURN expression
| x | y  | body  |
------------------
| 3 | -1 | {2,4} |
| 3 |  0 | {3}   |
| 3 |  2 | {1,5} |
| 4 | -1 | {3,5} |
| 4 |  0 | {4}   |
| 4 |  2 | {2,6} |
| 5 | -1 | {4,6} |
| 5 |  0 | {5}   |
| 5 |  2 | {3,7} |
------------------

"body"列のすべてのセットの共用体を計算し、最終セットが生成されます。

前述の説明から導き出されるのは、任意のユニバース・セットが空の場合、結果セット自体が空になるということです。たとえば、2つの複数割当て属性があり、次のように値が割り当てられているとします。
| xs    | ys    |
-----------------
| {1,2} | {3}   |
| {}    | {4,5} |
| {6}   | {}    |
-----------------
次のような文とします。
RETURN results AS
  SELECT
    xs,
    ys,
    FOREACH x IN xs, y IN ys RETURN (x + y) AS zs
  FROM InputState
次の結果が生成されます。
| xs    | ys    | zs    |
-------------------------
| {1,2} | {3}   | {4,5} |
| {}    | {4,5} | {}    |
| {6}   | {}    | {}    |
-------------------------

FOREACHの例

例1: これは、FOREACH式の最も簡単な例の1つです。複数割当ての整数属性(Score)が1つのみ使用され、RETURN式は各Scoreセットの値を返すだけです。
RETURN Results AS
SELECT
   WineID AS id,
   FOREACH x IN Score RETURN(x) AS ratings
FROM WineState
ORDER BY id
例2: この例では、2つの複数割当ての文字列属性(BodyおよびFlavors)が使用され、セットのメンバーが連結されます。
RETURN Results AS
SELECT
   WineID AS id,
   FOREACH x IN Body, y IN Flavors RETURN (CONCAT(x, ' ', y)) AS bodyflavor
FROM WineState
WHERE IS_NOT_EMPTY(Body) AND IS_NOT_EMPTY(Flavors)
ORDER BY id

WHERE句に2つのIS_NOT_EMPTY関数が使用され、空のセットが選択されないようにしていることに注意してください。

例3: この例では、LETFOREACHの両方が使用され、RETURN式にEXTRACT関数も使用されます。
RETURN Results AS
LET 
   (FOREACH d IN ShipDate RETURN (EXTRACT(d, YEAR))) AS yearSet
SELECT
   SET(Price) AS prices
FROM WineState
GROUP BY MEMBERS(yearSet) AS shipyear
この例では、ShipDateは複数割当てのdateTime属性です。属性dは、RETURN式内にのみ出現し、その式内の同じ名前の他の属性をシャドーイングします。yearSetSELECT句ではなくLET句で定義されているため、文の結果に出現しないことに注意してください。