Qwiratry β SQL-Grouping-Spitballing
SQL Grouping spitballing
This eventually became the separate module AggregateFunctions
Exploratory survey for Aggregation / Grouping in the Qwiratry language redesign (see SQL-Features-Plan.md Β§6, still TBR). Companion gap list: SQL-bits-missing.md (GROUP BY, WINDOW, Unique/DISTINCT). Research: SQL-AST-Qwiratry-Mapping.md.
Status: Spitballing / Outline.Scope: How other languages express grouping-shaped features, plus provisional prposed Qwiratry features.Non-scope: Backend SQL encoding, TaOP Relation APIs.
1. Grouping Survey
How existing languages and APIs express grouping, windows, and related aggregation features. Cells are representative idioms, not exhaustive catalogues. β means that family does not really own that column (or treats it as out of scope).
Rows mix SQL / query ASTs, host languages, dataframes, document/graph query
surfaces, MapReduce-family systems, and algebraic analogies. Use this when
designing Qwiratry operators for group-by, window, distinct, and aggregates
(vs todayβs ReduceOperator βΏ).
We don't need a column for HAVING, because we just do πΈ/Ο after the GROUP BY equivalent.
1.1 Column meanings
GROUP BY β Partition a relation (or stream) into groups by key(s), then typically collapse each group. Classical SQL
GROUP BY; dataframegroupby; RA Ξ³. Proposed Qwiratry naming: Partition + Aggregation.WINDOW β Compute over a peer partition without collapsing rows (
OVER (PARTITION BY β¦ ORDER BY β¦); rolling / sliding frames). Not the same as GROUP BY. Proposed Qwiratry naming: Partition (row-preserving).DISTINCT / Unique β Eliminate duplicate rows or values (
SELECT DISTINCT,.unique, RA Ξ΄). Not a fold to one accumulator.Aggregates β Per-group or global summary functions (
COUNT/SUM/ β¦). May appear with GROUP BY, as a global fold, or as window functions.Reduce with Partition β Whether the language has a reduce/fold-style combinator that runs per partition/key (MapReduce
reduce, SparkreduceByKey, KotlingroupingBy().reduce, β¦), as opposed to only a global reduce or only named aggregates (SUM) without a user fold.
| Language / surface | GROUP BY | WINDOW | DISTINCT / Unique | Aggregates | Reduce with Partition |
|---|---|---|---|---|---|
SQL DQL (SELECT) | GROUP BY columns / expressions | WINDOW β¦ AS; OVER (PARTITION BY β¦ ORDER BY β¦) | SELECT DISTINCT / DISTINCT ON (dialect) | COUNT / SUM / AVG / MIN / MAX / β¦ in select list | no REDUCE keyword; GROUP BY + aggregate β reduce-per-key; window aggs are not a fold |
| SQL (extended grouping) | GROUPING SETS / ROLLUP / CUBE | same window clause | β | same aggregates; GROUPING() helper | same as DQL (aggregates over each grouping set) |
| SQL::Abstract (AST) | SQL::Abstract::GroupBy | Window::Clauses / Definition / Function | Distinction / Distinction::Columns | via Function nodes | β (no reduce AST; aggregates are functions) |
| Red (AST / ResultSeq) | @.group on Select / Chained | β | func<DISTINCT> in projection | via classify / aggregates on ResultSeq | β (no partitioned reduce node; classify + fold in Raku) |
| Qwiratry today | β (no first-class group) | β | β (no UniqueOperator; not what βΏ does) | ReduceOperator (βΏ) fold β one accumulator | βΏ has no partition; whole subject is one bag |
| Proposed Qwiratry Symbols | Ξ³ (GroupByOperator) by key block / columns; collapses groups | Ο (WindowOperator) partition/order/frame; row-preserving (not Ξ³) | Ξ΄ (UniqueOperator / Distinct); not βΏ | aggregates with Ξ³, or global βΏ; window aggs on Ο | provisional: βΏ after / inside Ξ³ (reduce-per-partition); not yet an operator |
| Proposed Qwiratry Naming | Partition + Aggregation (partition keys, then collapse) | Partition (peers only; row-preserving) | Unique (name TBD vs Ξ΄) | Aggregation | Reduce over a Partition (name TBD; cf. βΏ + partition) |
| Proposed Raku Function | .aggreduce() | .aggmap() | |||
| MapReduce (Hadoop / model) | shuffle / group by key before reduce | β (batch model; not SQL WINDOW) | β (emit unique keys from map+combine patterns) | reduce outputs per key | yes β reduce(key, values) is the canonical partitioned reduce |
| Apache Spark (RDD / DF) | .groupBy / SQL GROUP BY; RDD groupByKey | .withColumn + Window.partitionBy; SQL OVER | .distinct / SQL DISTINCT | .agg / SQL aggregates | yes β reduceByKey / aggregateByKey / combineByKey |
| Apache Flink | keyed streams / keyBy + window/group aggs | event-time / count windows (stream windows β SQL OVER, related idea) | β | aggregate / sum / β¦ on keyed streams | yes β keyed reduce / FoldFunction on partitions |
Raku (List / Seq) | .classify / .categorize (Hash of lists) | β (no SQL-style window; sliding via rotor / supply) | .unique / Set / βͺ | .reduce / [+] / hyper ops; Bag counts | no built-in; pattern = classify then .reduce each group |
Raku (Bag / Mix) | weights already are per-key groups | β | Set projection of keys | .total / .elems / bag arithmetic | β (bag ops are not a user reduce-per-key API) |
| Kotlin | groupBy / groupingBy | β | .toSet / distinct helpers | aggregate / sum via stdlib | yes β groupingBy { }.reduce { β¦ } / fold / aggregate |
| Scala (collections / RDD) | .groupBy; Spark groupByKey | β in stdlib; Spark SQL windows | .distinct | .aggregate / .fold / .reduce | yes β Spark reduceByKey; collections: groupBy then reduce |
| Java Streams | Collectors.groupingBy | β | .distinct | Collectors.reducing / summarizing collectors | yes β groupingBy(classifier, reducing(β¦)) / downstream collectors |
| Pandas / DataFrame | .groupby(β¦) | .rolling / .expanding / .groupby(β¦).transform | .drop_duplicates / unique | .agg / .sum / .mean / β¦ | yes β groupby().agg / .apply / .transform with reducers |
| Polars | .group_by(β¦) | .over(β¦) (SQL-like window) | .unique | .agg | yes β group_by().agg (expressions); no separate reduce name |
| dplyr / R tidyverse | group_by() | window_* helpers; mutate after group_by keeps rows | distinct() | summarise() / n() / sum() / β¦ | partial β summarise is agg-per-group; reduce is purrr/global |
| LINQ / C# | .GroupBy | β (no first-class SQL WINDOW; custom) | .Distinct | .Aggregate / .Sum / .Count / β¦ | pattern β GroupBy then Aggregate / Sum on each group |
| Julia (DataFrames.jl) | groupby | β (or manual windows) | unique | combine / select with aggregation functions | yes β combine(groupby(β¦), β¦) / mapreduce over groups |
| NumPy / scientific Python | labels / unique + bincount patterns | sliding via stride tricks | np.unique | ufunc.reduce (global) | yes β ufunc.reduceat / segmented reduce; bincount |
| MATLAB | findgroups / splitapply / table groupsummary | β | unique | accumarray / sum / β¦ | yes β accumarray / splitapply (reduce per group index) |
| Wolfram Language | GroupBy / GatherBy | β (no SQL OVER; MovingMap / TimeSeriesAggregate for sliding) | DeleteDuplicates / DeleteDuplicatesBy | Total / Mean / Count / β¦; often via GroupBy red | yes β GroupBy[list, f, red] (docs: map-reduce generalization) |
| Racket | group-by (racket/list); Sawzall group-with; Asa table-groupby | β | remove-duplicates; Asa table-distinct | foldl / for/fold; Sawzall aggregate; Asa group-sum / β¦ | yes β Asa group-fold; Sawzall aggregate after group-with; else group then fold |
| XQuery 3 / FLWOR | group by clause | tumbling/sliding window clauses | distinct-values() | count / sum / avg / min / max | β (aggregates in return; no named reduce-per-group) |
| XSLT 3.0 | xsl:for-each-group | β (streaming / accumulators, not SQL WINDOW) | distinct-values | accumulators; count / sum in expressions | pattern β group then fold in template body |
| MongoDB aggregation | $group | $setWindowFields | $group + _id uniqueness; $addToSet | $sum / $avg / $min / $max / $push / β¦ | $group accumulators β reduce-per-key; no reduce stage name |
| Cypher (Neo4j) | implicit / WITH + aggregation keys | β (limited; no full SQL WINDOW in core) | DISTINCT | count / sum / avg / collect / β¦ | β (aggregation with keys; no reduce API) |
| SPARQL (query) | GROUP BY | β (no SQL WINDOW in SPARQL 1.1) | DISTINCT / REDUCED | COUNT / SUM / AVG / MIN / MAX / GROUP_CONCAT | β (aggregates + GROUP BY only) |
| Elasticsearch aggregations | bucket aggs (terms / histogram / β¦) | β (pipeline aggs; not SQL WINDOW) | cardinality / rare terms (approx unique) | metric aggs (sum / avg / β¦) | metrics under buckets β reduce-per-bucket |
| awk / Unix text | associative arrays keyed by fields | β | seen[$0]++ / sort | uniq | sum +=; length(a) counts | pattern β per-key sum[k]+= in the same pass |
| Excel / spreadsheets | PivotTable row fields; GROUPBY (newer) | β (running total formulas; not OVER) | Remove Duplicates / UNIQUE | SUMIF / COUNTIF / pivot values | β (pivot values; no reduce API) |
| DAX / Power BI | auto by visual axes; SUMMARIZE | β (time intelligence β SQL WINDOW) | β (table distinct via VALUES / DISTINCT) | SUM / AVERAGE / COUNTROWS / β¦ | β (filter context; not a reduce combinator) |
| APL / J / K | keyed / key (K); compress / group idioms | windowed reduce / stencil | βͺ / nub | reduction / (+/ sum, etc.) | yes β keyed/group reduce idioms (K key; J/key patterns) |
| Relational algebra | Ξ³ (aggregation / grouping operator) | β (not classical RA) | Ξ΄ (duplicate elimination) | aggregate functions inside Ξ³ | Ξ³ is partitioned aggregation (fold-per-group in the algebra) |
| GraphQL (query language) | β (grouping left to resolvers / DB) | β | β | β (field resolvers may aggregate) | β |
Notes for Qwiratry design
GROUP BY vs WINDOW: GROUP BY collapses; WINDOW keeps row cardinality. Both need partition keys; do not overload one operator for both. Proposed naming uses Partition for both, distinguished by whether Aggregation collapses the partition.
HAVING vs Ο: No HAVING column in the survey β use
πΈ/Οafter the GROUP BY equivalent (SQL-Features-Plan.md Β§3.1).DISTINCT vs βΏ:
ReduceOperatorfolds to one value; DISTINCT removes duplicates and yields a set-like relation. SQL-bits-missingβs βUniqueβ is the latter.Qwiratry today only has βΏ (and Ο / Ξ / sort). GROUP BY / WINDOW / DISTINCT / partitioned reduce are still β.
Proposed Qwiratry Symbols (provisional): Ξ³ / Ο / Ξ΄; βΏ remains fold. Proposed Qwiratry Naming: Partition, Aggregation, Unique, Reduce-over-Partition (TBD). Not normative until Plan Β§6 is written.
Reduce with Partition is the missing bridge between βΏ and GROUP BY: many languages expose it explicitly (MapReduce,
reduceByKey,groupingBy().reduce).Under the hood: SQL
WITH(CTE) can materialise grouped pipelines; not required in the language surface for a first cut.
GROUP BY vs WINDOW vs Qwiratry Reduce (βΏ)
Side-by-side of SQLβs two partition styles and todayβs Qwiratry fold. Reduce is not a stand-in for either SQL feature; it only covers the βcollapse the whole input with a combining functionβ corner.
| Dimension | SQL GROUP BY | SQL WINDOW / OVER | Qwiratry ReduceOperator (βΏ) |
|---|---|---|---|
| Purpose | Partition rows by key, then collapse each partition to (usually) one summary row | Partition (and optionally order) peers, then compute a value per input row | Fold the entire subject stream with a binary/&operation callable into one accumulator |
| Output cardinality | About one row per group (fewer than input, unless every key is unique) | Same as input (one result column/value per row; row-preserving) | One result (or a singleton list of the fold), not one-per-group |
| Partition / key | GROUP BY expressions | PARTITION BY inside OVER (optional) | None β no group key; whole relation is one bag |
| Order within partition | Implementation-defined unless paired with ordered aggregates / dialect extras | ORDER BY inside OVER (often required for ranking / running totals) | Only whatever order the upstream pipeline already has; not part of βΏ |
| Frame | N/A (whole group) | Rows/range/groups frame (ROWS BETWEEN β¦) | N/A |
| Aggregates | SUM / COUNT / β¦ over each group | Same functions (and ranking fns) as window functions over the frame | User-supplied fold only ($^a + $^b.age, etc.); no built-in SUM/COUNT catalogue |
| Filter after | HAVING (on group keys / aggregates) β in Qwiratry: Ο after group | Outer WHERE / qualify in enclosing query | Further Ο on the single result (rarely useful); not per-group |
| Typical SQL sketch | SELECT dept, SUM(sal) FROM t GROUP BY dept | SELECT *, SUM(sal) OVER (PARTITION BY dept) FROM t | (no SQL twin) β foldl / Raku [+] @vals over the whole list |
| Qwiratry today | β | β | Exists: slang βΏ, AST ReduceOperator, eager map-reduce eval |
| Proposed Qwiratry Symbols (provisional) | Ξ³ GroupByOperator (+ aggregates / Ο-after as HAVING) | Ο WindowOperator | Keep βΏ as global fold; may also serve as the combiner inside a group after Ξ³ |
| Proposed Qwiratry Naming | Partition + Aggregation | Partition | Reduce (global); Reduce with Partition TBD |
Takeaways
GROUP BY β many reduces (one fold per key), then emit group rows.
WINDOW β annotate each row using neighbours in a partition; it does not replace GROUP BY and does not match βΏ.
βΏ alone cannot express
GROUP BY deptorSUM(β¦) OVER (PARTITION BY dept): it has no partition and collapses to a single value.Designing Ξ³ / Ο as separate operators (see Proposed Qwiratry Symbols) avoids overloading βΏ for all three jobs; a partitioned reduce may still be the implementation bridge (or a fourth surface name).