Repository navigation
Decorrelate scalar subqueries with more complex filter expressions #14554
Copy link
Copy link
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Is your feature request related to a problem or challenge?
Datafusion already support decorrelating simple scalar subqueries in this PR: #6457
This follow the first approach in TUM paper (simple unnesting), and allow decorrelating this simple query
explain select t1.t1_int from t1 where (select count(*) from t2 where t1.t1_id = t2.t2_id) < t1.t1_int
However, if we add an or condition this subquery
explain select t1.t1_int from t1 where (select count(*) from t2 where t1.t1_id = t2.t2_id or t1.t1_name=t2.t2_name) < t1.t1_int
Datafusion cannot decorrelate it
+--------------+----------------------------------------------------------------------------------------+
| plan_type | plan |
+--------------+----------------------------------------------------------------------------------------+
| logical_plan | Projection: t1.t1_int |
| | Filter: (<subquery>) < CAST(t1.t1_int AS Int64) |
| | Subquery: |
| | Projection: count(*) |
| | Aggregate: groupBy=[[]], aggr=[[count(Int64(1)) AS count(*)]] |
| | Filter: outer_ref(t1.t1_id) = t2.t2_id OR outer_ref(t1.t1_name) = t2.t2_name |
| | TableScan: t2 |
| | TableScan: t1 projection=[t1_id, t1_name, t1_int] |
+--------------+----------------------------------------------------------------------------------------+
Describe the solution you'd like
Support decorrelating this query following the second method mentioned in the paper
Describe alternatives you've considered
No response
Additional context
General framework for decorrelation maybe discussed here #5492
But the steps needed to make this work is followed
Allow decorrelation for this type of filter exprs in this code:
| self.can_pull_over_aggregation = self.can_pull_over_aggregation |
Add more logic to handle complex query decorrelation:
- Build domain/magic relation
- Rewrite the subquery to join inner table (table of the subquery) with domain/magic relation using its complex filter expression (i.e
t2.t2_id = domain.t1_id OR t2.t2_name = domain.t1_name) - Rewrite aggregation to group by the additional columns mentioned in the domain/magic relation
- Join the outer relation with the newly built aggregation
For example the above mentioned query may be rewritten like
explain select t1.t1_int from t1,
(
select count(*) as count_all, domain.t1_id as t1_id, domain.t1_name as t1_name from (
select distinct t1_id, t1_name from t1
) as domain join t2 where t2.t2_id = domain.t1_id or t2.t2_name=domain.t1_name
group by domain.t1_id, domain.t1_name
) as pulled_up
where t1.t1_id=pulled_up.t1_id and pulled_up.count_all < t1.t1_int
Logical plan may look like
| logical_plan | Projection: t1.t1_int |
| | Inner Join: t1.t1_id = pulled_up.t1_id Filter: pulled_up.count_all < CAST(t1.t1_int AS Int64) |
| | TableScan: t1 projection=[t1_id, t1_int] |
| | SubqueryAlias: pulled_up |
| | Projection: count(*) AS count_all, domain.t1_id |
| | Aggregate: groupBy=[[domain.t1_id, domain.t1_name]], aggr=[[count(Int64(1)) AS count(*)]] |
| | Projection: domain.t1_id, domain.t1_name |
| | Inner Join: Filter: t2.t2_id = domain.t1_id OR t2.t2_name = domain.t1_name |
| | SubqueryAlias: domain |
| | Aggregate: groupBy=[[t1.t1_id, t1.t1_name]], aggr=[[]] |
| | TableScan: t1 projection=[t1_id, t1_name] |
| | TableScan: t2 projection=[t2_id, t2_name]
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
duongcongtoai commentedon Feb 9, 2025
take
alamb commentedon Feb 15, 2025
What do you think about implementing the more general approach to subquery unnesting described in that paper?
I think @xudong963 mentioned he had done something similar before
duongcongtoai commentedon Feb 21, 2025
From what is see in current code, this struct
PullUpCorrelatedExpris applied for scalar subquery as well as predicate subquery.For that paper implementation, i'll try my best to find time and figure out what usecases Datafusion cannot yet support. Will need to do it in steps/PRs
alamb commentedon Feb 22, 2025
FWIW I think @xudong963 said he has experience implementing such code so perhaps he will be able to help / assist with the implementation and review
xudong963 commentedon Feb 26, 2025
Yes, please ping me @duongcongtoai in your PR
duongcongtoai commentedon Mar 17, 2025
From this PR, there are several types of query mentioned that need support
This i cannot find any example query
I'll start thinking about implementing unnesting for all these usecases
ctsk commentedon Mar 31, 2025
Hey @duongcongtoai,
I want to draw your attention on a follow-up paper on "Unnesting Arbitrary Queries": https://15799.courses.cs.cmu.edu/spring2025/papers/11-unnesting/neumann-btw2025.pdf
This paper improves on the original approach by better dealing with multiple nesting levels. It also describes the process in an algorithmic way that might be closer to the implementation
duongcongtoai commentedon Apr 3, 2025
thank you, i'll take a look at the PR
duongcongtoai commentedon Apr 12, 2025
I think we can break down this story into multiple step:
alamb commentedon Apr 17, 2025
I really like the idea of the incremental approach -- I think it is practically speaking the only one we are likely to be able to pull off. Thank you @duongcongtoai
There are a bunch of related tickets listed on this epic:
What do you think about creating a new ticket with the steps you outline above @duongcongtoai ? I am pretty sure others are interested in this feature as well and may be able to help
duongcongtoai commentedon Apr 27, 2025
I think we can reuse this ticket right?: #5492
xudong963 commentedon Apr 27, 2025
Also, there is a newer paper for the topic: https://15799.courses.cs.cmu.edu/spring2025/papers/11-unnesting/neumann-btw2025.pdf
duongcongtoai commentedon Apr 28, 2025
Yes, that paper basically gave pretty neat skeleton for a decorrelation framework
alamb commentedon Apr 28, 2025
alamb commentedon May 18, 2025
I recommend we continue the discussion on