GitHub user pepijnve edited a discussion: Multiple 'group by's, one scan

In the system I'm working on I want to perform multiple aggregates using 
different group by criteria over large data sets.
I don't think grouping sets are an option since those support computing a 
single set of aggregates over multiple groupings. What I'm trying to achieve 
instead is one multiple sets of aggregates that each have their own group by 
strategy.

A simple way to do this is to just run multiple queries of course. That works 
but requires scanning through the data multiple times. That becomes prohibitive 
pretty quickly as the number of sets of aggregates increases.
While I was experimenting with the multiple query approach and combining those 
into a single query using 'union all' I started wondering if I couldn't write 
an operator to have my cake and eat it. So rather than this:

```
select 1 as setid, k1 as groupkey1, count(1) as agg1, null as groupkey2, null 
as agg2 from table group by groupkey1
union all
select 2 as setid, null as groupkey1, null as agg1, k2 as groupkey2, sum(col1) 
as agg2 from table group by groupkey2
```

which results in a logical plan that sort of looks like this (edited for 
brevity/clarity)

```
Union
   Projection: 1 AS setid, k1 AS groupkey1, count(1) AS agg1, NULL AS 
groupkey2, NULL AS agg2
     Aggregate: groupBy=[k1], aggr=[[count(1)]] |
       TableScan: table
   Projection: 2 AS setid, NULL AS groupkey1, NULL AS agg1, k2 AS groupkey2, 
count(1) AS agg2
     Aggregate: groupBy=[k2], aggr=[[sum(col1)]]
       TableScan: table 
```

what I would want to do instead is something like this

```
Unify
   Projection: 1 AS setid, k1 AS groupkey1, count(1) AS agg1, NULL AS 
groupkey2, NULL AS agg2
     Aggregate: groupBy=[k1], aggr=[[count(1)]] |
       CommonInputPlaceholder
   Projection: 2 AS setid, NULL AS groupkey1, NULL AS agg1, k2 AS groupkey2, 
count(1) AS agg2
     Aggregate: groupBy=[k2], aggr=[[sum(col1)]]
       CommonInputPlaceholder
   CommonInput
      TableScan: table 
```

`CommonInputPlaceholder` is a stub node that has the same schema as the 
`CommonInput` child.
The Unify operator works by setting up queues for each 
`CommonInputPlaceholder`. It polls the `CommonInput` child, and places a 
duplicate of each record batch it receives onto each queue. This is kind of 
similar to how RepartitionExec does its thing but instead of assigning each 
record batch once, we duplicate and assign it multiple times.

With quite some trial and error I've been able to get something up and running, 
but I have a feeling I'm going against the grain of the framework. Getting the 
optimizer to do the right thing for instance proved to be a challenge since it 
expects plans to be trees rather than DAGs.

My question for the group is if someone else has tried to implement something 
like this before? Or if what I'm trying to accomplish can be done in some other 
way? Perhaps someone has advice on how to best go about implementing this?
I realize this colors outside the lines of what you can express in SQL (as far 
as I know at least). I'm creating my queries by directly instantiating logical 
plans so for now that's not an issue for the system I'm working on.

Edit: I accidentally ended up writing an example that _can_ be done with 
grouping sets since the sets of aggregates were identical. Update example to 
use different aggregates.

GitHub link: https://github.com/apache/datafusion/discussions/15982

----
This is an automatically sent email for github@datafusion.apache.org.
To unsubscribe, please send an email to: 
github-unsubscr...@datafusion.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: github-unsubscr...@datafusion.apache.org
For additional commands, e-mail: github-h...@datafusion.apache.org

Reply via email to