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 SourceEQL 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 Sourcethen 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