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; dataframe groupby; 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, Spark reduceByKey, Kotlin groupingBy().reduce, …), as opposed to only a global reduce or only named aggregates (SUM) without a user fold.

Language / surfaceGROUP BYWINDOWDISTINCT / UniqueAggregatesReduce with Partition
SQL DQL (SELECT)GROUP BY columns / expressionsWINDOW … AS; OVER (PARTITION BY … ORDER BY …)SELECT DISTINCT / DISTINCT ON (dialect)COUNT / SUM / AVG / MIN / MAX / … in select listno REDUCE keyword; GROUP BY + aggregate β‰ˆ reduce-per-key; window aggs are not a fold
SQL (extended grouping)GROUPING SETS / ROLLUP / CUBEsame window clauseβ€”same aggregates; GROUPING() helpersame as DQL (aggregates over each grouping set)
SQL::Abstract (AST)SQL::Abstract::GroupByWindow::Clauses / Definition / FunctionDistinction / Distinction::Columnsvia Function nodesβ€” (no reduce AST; aggregates are functions)
Red (AST / ResultSeq)@.group on Select / Chainedβ€”func<DISTINCT> in projectionvia 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 NamingPartition + Aggregation (partition keys, then collapse)Partition (peers only; row-preserving)Unique (name TBD vs δ)AggregationReduce 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 keyyes β€” 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 aggregatesyes β€” reduceByKey / aggregateByKey / combineByKey
Apache Flinkkeyed streams / keyBy + window/group aggsevent-time / count windows (stream windows β‰  SQL OVER, related idea)β€”aggregate / sum / … on keyed streamsyes β€” 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 countsno 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)
KotlingroupBy / groupingByβ€”.toSet / distinct helpersaggregate / sum via stdlibyes β€” groupingBy { }.reduce { … } / fold / aggregate
Scala (collections / RDD).groupBy; Spark groupByKeyβ€” in stdlib; Spark SQL windows.distinct.aggregate / .fold / .reduceyes β€” Spark reduceByKey; collections: groupBy then reduce
Java StreamsCollectors.groupingByβ€”.distinctCollectors.reducing / summarizing collectorsyes β€” 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.aggyes β€” group_by().agg (expressions); no separate reduce name
dplyr / R tidyversegroup_by()window_* helpers; mutate after group_by keeps rowsdistinct()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)uniquecombine / select with aggregation functionsyes β€” combine(groupby(…), …) / mapreduce over groups
NumPy / scientific Pythonlabels / unique + bincount patternssliding via stride tricksnp.uniqueufunc.reduce (global)yes β€” ufunc.reduceat / segmented reduce; bincount
MATLABfindgroups / splitapply / table groupsummaryβ€”uniqueaccumarray / sum / …yes β€” accumarray / splitapply (reduce per group index)
Wolfram LanguageGroupBy / GatherByβ€” (no SQL OVER; MovingMap / TimeSeriesAggregate for sliding)DeleteDuplicates / DeleteDuplicatesByTotal / Mean / Count / …; often via GroupBy redyes β€” GroupBy[list, f, red] (docs: map-reduce generalization)
Racketgroup-by (racket/list); Sawzall group-with; Asa table-groupbyβ€”remove-duplicates; Asa table-distinctfoldl / for/fold; Sawzall aggregate; Asa group-sum / …yes β€” Asa group-fold; Sawzall aggregate after group-with; else group then fold
XQuery 3 / FLWORgroup by clausetumbling/sliding window clausesdistinct-values()count / sum / avg / min / maxβ€” (aggregates in return; no named reduce-per-group)
XSLT 3.0xsl:for-each-groupβ€” (streaming / accumulators, not SQL WINDOW)distinct-valuesaccumulators; count / sum in expressionspattern β€” 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)DISTINCTcount / sum / avg / collect / …— (aggregation with keys; no reduce API)
SPARQL (query)GROUP BYβ€” (no SQL WINDOW in SPARQL 1.1)DISTINCT / REDUCEDCOUNT / SUM / AVG / MIN / MAX / GROUP_CONCATβ€” (aggregates + GROUP BY only)
Elasticsearch aggregationsbucket 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 textassociative arrays keyed by fieldsβ€”seen[$0]++ / sort | uniqsum +=; length(a) countspattern β€” per-key sum[k]+= in the same pass
Excel / spreadsheetsPivotTable row fields; GROUPBY (newer)β€” (running total formulas; not OVER)Remove Duplicates / UNIQUESUMIF / COUNTIF / pivot valuesβ€” (pivot values; no reduce API)
DAX / Power BIauto by visual axes; SUMMARIZEβ€” (time intelligence β‰  SQL WINDOW)β€” (table distinct via VALUES / DISTINCT)SUM / AVERAGE / COUNTROWS / …— (filter context; not a reduce combinator)
APL / J / Kkeyed / key (K); compress / group idiomswindowed reduce / stencilβˆͺ / nubreduction / (+/ 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 ⌿: ReduceOperator folds 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.

DimensionSQL GROUP BYSQL WINDOW / OVERQwiratry ReduceOperator (⌿)
PurposePartition rows by key, then collapse each partition to (usually) one summary rowPartition (and optionally order) peers, then compute a value per input rowFold the entire subject stream with a binary/&operation callable into one accumulator
Output cardinalityAbout 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 / keyGROUP BY expressionsPARTITION BY inside OVER (optional)None β€” no group key; whole relation is one bag
Order within partitionImplementation-defined unless paired with ordered aggregates / dialect extrasORDER BY inside OVER (often required for ranking / running totals)Only whatever order the upstream pipeline already has; not part of ⌿
FrameN/A (whole group)Rows/range/groups frame (ROWS BETWEEN …)N/A
AggregatesSUM / COUNT / … over each groupSame functions (and ranking fns) as window functions over the frameUser-supplied fold only ($^a + $^b.age, etc.); no built-in SUM/COUNT catalogue
Filter afterHAVING (on group keys / aggregates) β€” in Qwiratry: Οƒ after groupOuter WHERE / qualify in enclosing queryFurther Οƒ on the single result (rarely useful); not per-group
Typical SQL sketchSELECT dept, SUM(sal) FROM t GROUP BY deptSELECT *, 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)Ο‰ WindowOperatorKeep ⌿ as global fold; may also serve as the combiner inside a group after Ξ³
Proposed Qwiratry NamingPartition + AggregationPartitionReduce (global); Reduce with Partition TBD

Takeaways

  1. GROUP BY β‰ˆ many reduces (one fold per key), then emit group rows.

  2. WINDOW β‰ˆ annotate each row using neighbours in a partition; it does not replace GROUP BY and does not match ⌿.

  3. ⌿ alone cannot express GROUP BY dept or SUM(…) OVER (PARTITION BY dept): it has no partition and collapses to a single value.

  4. 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).

Qwiratry v0.11.0

Declarative query and data-walking architecture for Raku, with transformers, molds, walkers, I/O pipelines, and aggregate functions.

Authors

  • Tim Nelson

License

Dependencies

SlangifyImplementation::Loader:ver<0.0.9+>Glob::Grammar

Test Dependencies

Provides

  • Qwiratry
  • Qwiratry::Aggregate::AggSub
  • Qwiratry::Aggregate::AggSub::Compiler
  • Qwiratry::Aggregate::Avg
  • Qwiratry::Aggregate::BodyRewrite
  • Qwiratry::Aggregate::Count
  • Qwiratry::Aggregate::DenseRank
  • Qwiratry::Aggregate::Driver
  • Qwiratry::Aggregate::Rank
  • Qwiratry::Aggregate::Sum
  • Qwiratry::AggregateFunction
  • Qwiratry::Context
  • Qwiratry::DatabaseStorage
  • Qwiratry::DatabaseStorage::CSVdemo
  • Qwiratry::DatabaseStorage::Location
  • Qwiratry::DatabaseStorage::Location::Tree
  • Qwiratry::DatabaseStorage::Memory
  • Qwiratry::FieldMode
  • Qwiratry::Format
  • Qwiratry::Format::Base
  • Qwiratry::Format::CSVdemo
  • Qwiratry::Format::HalfHuman
  • Qwiratry::Format::JSONdemo
  • Qwiratry::Format::NDJSONdemo
  • Qwiratry::Format::Textish::RenderWalkable
  • Qwiratry::Implementation::Loader
  • Qwiratry::Location
  • Qwiratry::Location::Base
  • Qwiratry::Location::File
  • Qwiratry::Mold
  • Qwiratry::Mold::Compiler
  • Qwiratry::Mold::Registry
  • Qwiratry::Mold::Slang
  • Qwiratry::MovingFrameAggregateFunction
  • Qwiratry::Operator::Capability
  • Qwiratry::Operator::IO
  • Qwiratry::Operator::MapReduce
  • Qwiratry::Operator::Navigation
  • Qwiratry::Operator::Set
  • Qwiratry::Query::Evaluator::Eager
  • Qwiratry::Query::Evaluator::Filter
  • Qwiratry::Query::Evaluator::Join
  • Qwiratry::Query::Evaluator::Lazy
  • Qwiratry::Query::Evaluator::MapReduce
  • Qwiratry::Query::Evaluator::Navigation
  • Qwiratry::Query::Evaluator::Relational
  • Qwiratry::Query::Evaluator::Row
  • Qwiratry::Query::Evaluator::Set
  • Qwiratry::Query::Evaluator::Union
  • Qwiratry::Query::Extract
  • Qwiratry::Query::NamedJoins
  • Qwiratry::Query::RelationCommon
  • Qwiratry::Query::Runtime
  • Qwiratry::Query::Selector
  • Qwiratry::Query::Slang
  • Qwiratry::Query::Slang::Ops
  • Qwiratry::Query::Slang::Topic
  • Qwiratry::Query::Specificity
  • Qwiratry::Query::Topic
  • Qwiratry::QueryCursor
  • Qwiratry::QueryIterator
  • Qwiratry::QueryMatch
  • Qwiratry::Setup
  • Qwiratry::Singleton
  • Qwiratry::Strategy
  • Qwiratry::Strategy::ControlSignal
  • Qwiratry::Strategy::FinishResult
  • Qwiratry::Strategy::RewriteSpec
  • Qwiratry::Strategy::Traversal
  • Qwiratry::Suggest
  • Qwiratry::TaOP
  • Qwiratry::Table
  • Qwiratry::Table::Schema
  • Qwiratry::Transformer
  • Qwiratry::Transformer::Copy
  • Qwiratry::Transformer::TreeRewrite
  • Qwiratry::Tree::Navigator
  • Qwiratry::Tree::Navigator::Base
  • Qwiratry::Tree::Navigator::Filesystem
  • Qwiratry::Tree::Navigator::Match
  • Qwiratry::Tree::Navigator::RakuAST
  • Qwiratry::Tree::Replace
  • Qwiratry::Walker
  • Qwiratry::Walker::Capabilities
  • Qwiratry::Walker::Factory
  • Qwiratry::Walker::Implementation::Table
  • Qwiratry::Walker::Implementation::Tree
  • Qwiratry::Walker::Implementation::TreeWithPath
  • Qwiratry::Walker::Master
  • Qwiratry::Walker::Providing
  • TypedIterator
  • WalkCursor
  • X::Qwiratry

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite β€” the markup and publishing tools behind this site.