diff --git a/core-spec/expression_language.md b/core-spec/expression_language.md index 617aeefd..74cdaeed 100644 --- a/core-spec/expression_language.md +++ b/core-spec/expression_language.md @@ -217,7 +217,7 @@ APPROX_PERCENTILE(response_time, 0.95) ### Conditional Aggregations (REQUIRED) SUM / COUNT aggregation functions support `DISTINCT.` -All aggregations should support filtered aggregation: +All aggregations must support filtered aggregation, expressed either with a `CASE` argument or with a postfix `FILTER (WHERE ...)` modifier: ```sql -- DISTINCT modifier @@ -227,6 +227,32 @@ COUNT(DISTINCT customer_id) -- Filtered aggregation via CASE SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) COUNT(CASE WHEN status = 'completed' THEN 1 END) + +-- Filtered aggregation via FILTER (WHERE ...) +SUM(amount) FILTER (WHERE status = 'completed') +COUNT(*) FILTER (WHERE status = 'completed') +``` + +#### `FILTER (WHERE ...)` (REQUIRED) + +Every aggregate function must support a postfix `FILTER (WHERE )` modifier: + +``` +() FILTER (WHERE ) +``` + +The clause has the semantics of the SQL:2003 `` (optional feature T612): the aggregate considers only the rows for which `` succeeds. `FILTER` is applied to the aggregate's input, not as a query `WHERE`, so it never removes output groups. + +`FILTER (WHERE ...)` is a modifier on an aggregate expression. It is not the standalone `WHERE` clause listed under [Not Supported in Expressions](#not-supported-in-expressions). The `` must reference only fields of the same dataset as the aggregate's arguments. + +Engines without native `FILTER (WHERE ...)` support MAY lower it to the equivalent `CASE` form: + +```sql +-- value aggregate: filter the argument +SUM(amount) FILTER (WHERE status = 'completed') --> SUM(CASE WHEN status = 'completed' THEN amount END) + +-- COUNT(*): filter a constant +COUNT(*) FILTER (WHERE status = 'completed') --> COUNT(CASE WHEN status = 'completed' THEN 1 END) ``` ### Decomposability Reference