alamb opened a new issue, #27233:
URL: https://github.com/apache/datafusion/issues/27233
### Is your feature request related to a problem or challenge?
There is a lot of great work from @SubhamSinghal, @jayzhan211 and others on
the "TopK per partition" optimization for window queries like
```sql
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY revenue DESC)
AS rn, *
FROM sales
) WHERE rn <= 5;
```
The original request is from 2023:
- https://github.com/apache/datafusion/issues/6899
The optimization landed in DataFusion 55 behind
`datafusion.optimizer.enable_window_topn`, and the EPIC to turn it on by
default is
- https://github.com/apache/datafusion/issues/24608
- https://github.com/apache/datafusion/pull/27197
As always, telling the community about it will be good
1. To advertise the feature for others who have the same use case,
2. To document in an easier to understand way how the feature works
2. The authors to raise their visibility.
I had drafted a section about it for the DataFusion 55 release post (see
below) but a release post isn't great as it has limited space to explain how
the operator works
- https://github.com/apache/datafusion/issues/24216
- https://github.com/apache/datafusion-site/pull/203
### Describe the solution you'd like
Thus I suggest a complete self contained blog post on
https://datafusion.apache.org/blog/ (make a PR to
https://github.com/apache/datafusion-site) about TopK per partition.
I suggest the standard structure of "informative post with low key
DataFusion pitch"
For example, the pattern of
- Dynamic Filters (TopK and hash join, 25x faster):
https://datafusion.apache.org/blog/2025/09/10/dynamic-filters/
- Fast high cardinality grouping:
https://datafusion.apache.org/blog/2023/08/05/datafusion_fast_grouping/
Here are some ideas for an outline:
1. **An understandable representative usecase**: "top N per group" (e.g
latest N events per device) so readers can tell if it applies to them. Mention
this is [db-benchmark](https://github.com/apache/datafusion/issues/13586) / h2o
query 8 shape.
2. **Background / technical intuition**: the naive plan sorts the entire
input to compute the window function and then throws almost all of it away. The
TopK per partition operator keeps a bounded heap per partition instead. Explain
how `ROW_NUMBER`, `RANK` and `DENSE_RANK` differ in what has to be retained.
3. **How to use it in DataFusion**: What version is it introduced in (57 I
hope, show `EXPLAIN` before/after with `PartitionedTopKExec`), and how to turn
it off)
4. **A bit about how it is implemented**: the `WindowTopN` physical
optimizer rule, `PartitionedTopKExec`, and then some of the highlights of
making it work for high cardinality:
- the 100K+ partition cliff,
https://github.com/apache/datafusion/pull/21479#issuecomment-4221809099
- Memory optimizations, etc
- Emitting the rank column directly from the operator so the window node
can be removed (#26133)
5. **Call for help** 🎣: Any remaining work that needs to happen
I think the best time to publish is around 57 release (when #27197 is merged
and released, so the post can say the optimization is on by default.
### Describe alternatives you've considered
Here is the original text I had drafted for the 55 release post (from
https://github.com/apache/datafusion/issues/24608), which could be a starting
point:
<details><summary>Details</summary>
<p>
### Per-Partition TopK for Window Functions
A common analytics pattern selects the top N rows per group using a window
function:
```sql
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY revenue DESC)
AS rn, *
FROM sales
) WHERE rn <= 5;
```
DataFusion previously sorted the *entire* input to evaluate the window
function, even though only a handful of rows per partition survive the
filter.
DataFusion 55 recognizes this pattern and uses a new `PartitionedTopKExec`
operator that keeps only the top N rows per partition, dramatically reducing
sorting and memory for high-cardinality inputs. The optimization applies to
`ROW_NUMBER` and `RANK`, resolving a feature request first filed in 2023
(#6899).
Thanks to @SubhamSinghal for implementing this feature, with reviews from
@2010YOUY01 and @kosiew. Related PRs: #21479, #22885, #23096
</p>
</details>
### Additional context
Tracking issues:
- https://github.com/apache/datafusion/issues/6899 (original 2023 request)
- https://github.com/apache/datafusion/issues/24608 (enable by default EPIC)
- https://github.com/apache/datafusion/issues/24060 (follow on optimizations)
- https://github.com/apache/datafusion/issues/23197 (window function
performance EPIC)
Core operator and optimizer rule:
- https://github.com/apache/datafusion/pull/21479 (`ROW_NUMBER`,
@SubhamSinghal)
- https://github.com/apache/datafusion/pull/22885 (`RANK`, @SubhamSinghal)
- https://github.com/apache/datafusion/pull/23869 (`DENSE_RANK`,
@SubhamSinghal)
- https://github.com/apache/datafusion/pull/24191 (run `WindowTopN` before
`EnforceRequirements`, @saadtajwar)
- https://github.com/apache/datafusion/pull/23355 (range partitioned inputs,
@stuhood)
- https://github.com/apache/datafusion/pull/26133 (emit the rank column from
the operator, @SubhamSinghal)
- https://github.com/apache/datafusion/pull/27197 (enable by default,
@SubhamSinghal)
Closing the high cardinality gap:
- https://github.com/apache/datafusion/pull/23096 (share encoder and
reservation across partitions)
- https://github.com/apache/datafusion/pull/24591 (`RANK` memory from
O(input) to O(K + ties), @jayzhan211)
- https://github.com/apache/datafusion/pull/25468 (interleave retained rows
at emit)
- https://github.com/apache/datafusion/pull/25730 (shared `RecordBatchStore`
for `ROW_NUMBER`)
- https://github.com/apache/datafusion/pull/25968 (`RANK` on the shared
store with decide-then-gather)
Correctness fixes and metrics:
- https://github.com/apache/datafusion/pull/24405 (@viirya)
- https://github.com/apache/datafusion/pull/23599 (@zhuqi-lucas)
- https://github.com/apache/datafusion/pull/24495 (metrics, @shinzoxD)
- https://github.com/apache/datafusion/pull/26417 (filter column through
projections)
- https://github.com/apache/datafusion/pull/25535 (@jayzhan211)
Benchmarks:
- https://github.com/apache/datafusion/pull/24050 (`dense_rank` benchmark)
- https://github.com/apache/datafusion/pull/24732 (h2o `window_sorted` sweep)
Open follow on work:
- https://github.com/apache/datafusion/issues/26381 (`Utf8View` buffers kept
alive)
- https://github.com/apache/datafusion/issues/26876 /
https://github.com/apache/datafusion/pull/27136 (`DENSE_RANK` on the shared
store)
- https://github.com/apache/datafusion/issues/21596 (filters with embedded
projections)
- https://github.com/apache/datafusion/pull/26160 (`rn = 1`)
- https://github.com/apache/datafusion/issues/23600 (`ROW_NUMBER() = 1` /
`DISTINCT ON` dedup)
- https://github.com/apache/datafusion/pull/24629 (streaming path for
pre-sorted input, closed but a good idea)
Related prior posts for style / context:
- [Optimizing for Almost Sorted Data: Sort
Pushdown](https://datafusion.apache.org/blog/2026/07/20/sort-pushdown/)
- [Dynamic Filters](https://github.com/apache/datafusion-site/pull/103)
- Previous blog post tickets:
https://github.com/apache/datafusion/issues/15513,
https://github.com/apache/datafusion/issues/24667
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]