The FOREACH function performs a computation on every member of a set or sets and assembles the results into a set.
FOREACH may be used in any context within an EQL statement that accepts expressions: LET, SELECT, row function or aggregator arguments, WHERE, HAVING, or ORDER BY. Because FOREACH always evaluates to a set, the context must accept set-typed expressions, or EQL will signal a checking error.
FOREACH id_1 IN set_1[, id_n IN set_n] RETURN(expression)where:
At a minimum, you must specify one identifier/set pair (id IN set) and the RETURN expression.
FOREACH binds the id_1 through id_n identifiers within the RETURN expression; they are not in scope in the universes set_1 through set_n. These bindings shadow any other bindings for id_1 through id_n that may be in scope at the point of the FOREACH function.
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 interprets the reference to
results.x (in the
FOREACH
RETURN expression) as a reference to the
x defined by the
SELECT clause (that is, as an alias for
Source.x and not as a reference to the
x bound by
FOREACH). Similarly, EQL interprets the reference
Source.y as a reference to the attribute
y in
Source.
RETURN results AS
SELECT
Source.x AS x,
FOREACH x IN {1, 2}, y in {3, 4} RETURN (x + y) AS vals
FROM Source
then EQL interprets
x and
y (in the
FOREACH
RETURN expression) as references to the
FOREACH-bound identifiers, even though
x and
y are already in scope from the earlier
SELECT and from the data source. Therefore,
vals always has the value {4, 5, 6}.
FOREACH x IN {1, 2}, y IN {'abc', 'def'} RETURN (x + y)
Here, the first universe is a set of integers, and thus
x has type integer within the
RETURN expression. Similarly, the second universe is a
set of strings, so
y has type string. As a result, the
RETURN expression
x + y is thus ill-typed (and EQL signals an error accordingly).
If the RETURN expression has atomic type t, then the entire FOREACH expression has type "set of t". Therefore, if the RETURN expression has type integer, then the containing FOREACH expression has type integer set.
If, on the other hand, the RETURN expression has a set type, then the FOREACH expression has the same type as the RETURN expression. (This corresponds to the case where FOREACH takes the union of the values of the RETURN expression.) So, if the RETURN expression produces a set of string, then the FOREACH expression does also.
RETURN results AS
SELECT
SET_UNIONS(FOREACH x IN e RETURN(b)) AS unions
FROM Source
GROUP
FOREACH x IN e RETURN(SUM(x))
FOREACH x IN {1, 2, 3} RETURN(x * 2 + 1)
This expression evaluates to the set {3, 5, 7}. This is because, notionally, EQL evaluates the RETURN expression x * 2 + 1 once for each member of the set {1, 2, 3}, with x taking on each element of that set in turn. Finally, EQL assembles the results of these evaluations into another set.
Because the universe and result are both sets, you cannot specify the order in which the traversal visits the elements of the universe, or the order in which the result values appear in the final set.
FOREACH x IN {1, 2, 3}, y IN {40, 50, 60} RETURN(x + y)
This expression evaluates to the set {41, 42, 43, 51, 52, 53, 61,
62, 63}. Here, EQL evaluates the
RETURN expression
x + y for all possible combinations of values
x and
y from the two sets:
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
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}
In the first example, ABS(-1) = ABS(1) = 1, so the final set
contains only two elements; the value 1 may not appear twice. In the second
example, TO_INTEGER('x') is NULL, so this value does not appear in the final
set.
FOREACH x in {3, 4, 5}, y IN {-1, 0, 2} RETURN ({x + y, x - y})
// 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} |
------------------
Computing the union of all of the sets in the "body" column produces the final result.
| 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} | {} | {} |
-------------------------
RETURN Results AS SELECT WineID AS id, FOREACH x IN Score RETURN(x) AS ratings FROM WineState ORDER BY id
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
Note that the WHERE clause uses two IS_NOT_EMPTY functions to prevent empty sets being selected.
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