[ 
https://issues.apache.org/jira/browse/CALCITE-7912?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

ASF GitHub Bot updated CALCITE-7912:
------------------------------------
    Labels: pull-request-available  (was: )

> Prune UNION ALL branches that cannot affect an enclosing aggregate
> ------------------------------------------------------------------
>
>                 Key: CALCITE-7912
>                 URL: https://issues.apache.org/jira/browse/CALCITE-7912
>             Project: Calcite
>          Issue Type: Improvement
>          Components: core
>    Affects Versions: 1.42.0
>            Reporter: Sean Broeder
>            Assignee: Sean Broeder
>            Priority: Major
>              Labels: pull-request-available
>
> A common pattern builds a view as a UNION ALL of branches that each fill only 
> some of the columns and produce NULL for the rest. For example:
> SELECT a, SUM(x) AS m, CAST(NULL AS INTEGER) AS b FROM t1 GROUP BY a
> UNION ALL
> SELECT a, CAST(NULL AS INTEGER), SUM(y) FROM t2 GROUP BY a
> A query such as SELECT SUM(m) FROM view only needs the first branch. The 
> second branch produces NULL for m, and SUM ignores NULL, so it cannot change 
> the result. Calcite still plans, and the engine still executes, the whole 
> second branch, including its scan of t2 and its aggregation.
> Proposal: Add AggregateUnionNullBranchRule as 
> CoreRules.AGGREGATE_UNION_NULL_BRANCH. It matches a logical Aggregate that 
> has at least one aggregate call over a logical UNION ALL. A branch is 
> irrelevant to the aggregate and can be removed when:
> - every aggregate call ignores NULL inputs (SUM, SUM0, MIN, MAX, AVG, 
> COUNT(col)), and
> - each call reads a column that the branch produces as a constant NULL.
> Then:
> - Without GROUP BY: the irrelevant branches are removed. At least one branch 
> is always kept.
> - With GROUP BY, including ROLLUP and GROUPING SETS: the branch is kept 
> because it still contributes group keys. A redundant pure GROUP BY that has 
> no aggregate calls inside it is replaced by a projection of the grouping 
> columns. Duplicate rows can't change the enclosing aggregate.
> DISTINCT and FILTER on the aggregate calls are preserved.
> Multi-argument COUNT: COUNT(a, b) skips a row if any argument is NULL. A 
> branch therefore contributes nothing as soon as one argument is always NULL 
> in that branch, and the rule treats it as irrelevant. For example:
> SELECT COUNT(k, m) FROM (
>    SELECT mgr AS k, SUM(sal) AS m, CAST(NULL AS INTEGER) AS b FROM emp GROUP 
> BY mgr
>    UNION ALL
>    SELECT mgr, CAST(NULL AS INTEGER), SUM(empno) FROM emp GROUP BY mgr) AS v
> The plan starts as COUNT($0, $1) over the union. The second branch is removed 
> because its m is always NULL. The result is COUNT($0) over the first branch. 
> RelBuilder drops arguments of a non-DISTINCT COUNT that cannot be NULL, and 
> the rule re-creates the aggregate through it. On the surviving branch m is 
> SUM(sal) over a NOT NULL column, so it is dropped. IIn the same way, COUNT(m) 
> becomes COUNT() when m cannot be NULL.
> Not changed:
> - COUNT(*) is never treated as ignoring NULL, so such a branch is kept.
> - UNION distinct is not rewritten.
> - Branches that pad with a non-NULL constant, or with a nullable column that 
> is not constant NULL, are kept.
> - Aggregates not known to ignore NULL, for example ANY_VALUE, keep the branch.
> - A ROLLUP or other non-simple pure GROUP BY inside a branch is not removed.
> - A pure GROUP BY or DISTINCT over a union, with no aggregate calls, is not 
> touched.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to