expressions cannot contain aggregate functions or window functions).
</para>
- <para>
- PostgreSQL also supports the syntax <literal>GROUP BY ALL</literal>,
- which is equivalent to explicitly writing all select-list entries that
- do not contain either an aggregate function referring to the same query level or a window function.
- This can greatly simplify ad-hoc exploration of data.
- As an example, these queries are equivalent:
-<screen>
-<prompt>=></prompt> <userinput>SELECT a, b, a + b, sum(c) FROM test1 GROUP BY ALL;</userinput>
- a | b | ?column? | sum
----+---+----------+----
- 1 | 4 | 5 | 9
- 2 | 5 | 7 | 12
- 3 | 6 | 9 | 15
-(3 rows)
-
-<prompt>=></prompt> <userinput>SELECT a, b, a + b, sum(c) FROM test1 GROUP BY a, b, a + b;</userinput>
- a | b | ?column? | sum
----+---+----------+----
- 1 | 4 | 5 | 9
- 2 | 5 | 7 | 12
- 3 | 6 | 9 | 15
-(3 rows)
-</screen>
- </para>
-
<indexterm>
<primary>HAVING</primary>
</indexterm>
[ { * | <replaceable class="parameter">expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] } [, ...] ]
[ FROM <replaceable class="parameter">from_item</replaceable> [, ...] ]
[ WHERE <replaceable class="parameter">condition</replaceable> ]
- [ GROUP BY { ALL | [ ALL | DISTINCT ] <replaceable class="parameter">grouping_element</replaceable> [, ...] } ]
+ [ GROUP BY [ ALL | DISTINCT ] <replaceable class="parameter">grouping_element</replaceable> [, ...] ]
[ HAVING <replaceable class="parameter">condition</replaceable> ]
[ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceable class="parameter">window_definition</replaceable> ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] <replaceable class="parameter">select</replaceable> ]
<para>
The optional <literal>GROUP BY</literal> clause has the general form
<synopsis>
-GROUP BY { ALL | [ ALL | DISTINCT ] <replaceable class="parameter">grouping_element</replaceable> [, ...] }
+GROUP BY [ ALL | DISTINCT ] <replaceable class="parameter">grouping_element</replaceable> [, ...]
</synopsis>
</para>
input-column name rather than an output column name.
</para>
- <para>
- The form <literal>GROUP BY ALL</literal> with no explicit
- <replaceable class="parameter">grouping_elements</replaceable>
- provided is equivalent to writing <literal>GROUP BY</literal> with the
- numbers of all <command>SELECT</command> output columns that do not
- contain either an aggregate function referring to the same query level or
- a window function.
- </para>
-
<para>
If any of <literal>GROUPING SETS</literal>, <literal>ROLLUP</literal> or
<literal>CUBE</literal> are present as grouping elements, then the
INTO [ TEMPORARY | TEMP | UNLOGGED ] [ TABLE ] <replaceable class="parameter">new_table</replaceable>
[ FROM <replaceable class="parameter">from_item</replaceable> [, ...] ]
[ WHERE <replaceable class="parameter">condition</replaceable> ]
- [ GROUP BY { ALL | [ ALL | DISTINCT ] <replaceable class="parameter">grouping_element</replaceable> [, ...] } ]
+ [ GROUP BY [ ALL | DISTINCT ] <replaceable class="parameter">grouping_element</replaceable> [, ...] ]
[ HAVING <replaceable class="parameter">condition</replaceable> ]
[ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceable class="parameter">window_definition</replaceable> ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] <replaceable class="parameter">select</replaceable> ]
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
- stmt->groupByAll,
&qry->groupingSets,
&qry->targetList,
qry->sortClause,
EXPR_KIND_GROUP_BY,
false /* allow SQL92 rules */ );
qry->groupDistinct = stmt->groupDistinct;
- qry->groupByAll = stmt->groupByAll;
if (stmt->distinctClause == NIL)
{
typedef struct GroupClause
{
bool distinct;
- bool all;
List *list;
} GroupClause;
n->whereClause = $6;
n->groupClause = ($7)->list;
n->groupDistinct = ($7)->distinct;
- n->groupByAll = ($7)->all;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *) n;
n->whereClause = $6;
n->groupClause = ($7)->list;
n->groupDistinct = ($7)->distinct;
- n->groupByAll = ($7)->all;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *) n;
GroupClause *n = palloc_object(GroupClause);
n->distinct = $3 == SET_QUANTIFIER_DISTINCT;
- n->all = false;
n->list = $4;
$$ = n;
}
- | GROUP_P BY ALL
- {
- GroupClause *n = palloc_object(GroupClause);
- n->distinct = false;
- n->all = true;
- n->list = NIL;
- $$ = n;
- }
| /*EMPTY*/
{
GroupClause *n = palloc_object(GroupClause);
n->distinct = false;
- n->all = false;
n->list = NIL;
$$ = n;
}
n->whereClause = $4;
n->groupClause = ($5)->list;
n->groupDistinct = ($5)->distinct;
- n->groupByAll = ($5)->all;
n->havingClause = $6;
n->windowClause = $7;
n->sortClause = $8;
* GROUP BY items will be added to the targetlist (as resjunk columns)
* if not already present, so the targetlist must be passed by reference.
*
- * If GROUP BY ALL is specified, the groupClause will be inferred to be all
- * non-aggregate, non-window expressions in the targetlist.
- *
* This is also used for window PARTITION BY clauses (which act almost the
* same, but are always interpreted per SQL99 rules).
*
*
* pstate ParseState
* grouplist clause to transform
- * groupByAll is this a GROUP BY ALL statement?
* groupingSets reference to list to contain the grouping set tree
* targetlist reference to TargetEntry list
* sortClause ORDER BY clause (SortGroupClause nodes)
* useSQL99 SQL99 rather than SQL92 syntax
*/
List *
-transformGroupClause(ParseState *pstate, List *grouplist, bool groupByAll,
- List **groupingSets,
+transformGroupClause(ParseState *pstate, List *grouplist, List **groupingSets,
List **targetlist, List *sortClause,
ParseExprKind exprKind, bool useSQL99)
{
bool hasGroupingSets = false;
Bitmapset *seen_local = NULL;
- /* Handle GROUP BY ALL */
- if (groupByAll)
- {
- /* There cannot have been any explicit grouplist items */
- Assert(grouplist == NIL);
-
- /* Iterate over targets, adding acceptable ones to the result list */
- foreach_ptr(TargetEntry, tle, *targetlist)
- {
- /* Ignore junk TLEs */
- if (tle->resjunk)
- continue;
-
- /*
- * TLEs containing aggregates are not okay to add to GROUP BY
- * (compare checkTargetlistEntrySQL92). But the SQL standard
- * directs us to skip them, so it's fine.
- */
- if (pstate->p_hasAggs &&
- contain_aggs_of_level((Node *) tle->expr, 0))
- continue;
-
- /*
- * Likewise, TLEs containing window functions are not okay to add
- * to GROUP BY, and the SQL standard directs us to skip them.
- */
- if (pstate->p_hasWindowFuncs &&
- contain_windowfuncs((Node *) tle->expr))
- continue;
-
- /*
- * Otherwise, add the TLE to the result using default sort/group
- * semantics. We specify the parse location as the TLE's
- * location, despite the comment for addTargetToGroupList
- * discouraging that. The only other thing we could point to is
- * the ALL keyword, which seems unhelpful when there are multiple
- * TLEs.
- */
- result = addTargetToGroupList(pstate, tle,
- result, *targetlist,
- exprLocation((Node *) tle->expr));
- }
-
- /* If we found any acceptable targets, we're done */
- if (result != NIL)
- return result;
-
- /*
- * Otherwise, the SQL standard says to treat it like "GROUP BY ()".
- * Build a representation of that, and let the rest of this function
- * handle it.
- */
- grouplist = list_make1(makeGroupingSet(GROUPING_SET_EMPTY, NIL, -1));
- }
-
/*
* Recursively flatten implicit RowExprs. (Technically this is only needed
* for GROUP BY, per the syntax rules for grouping sets, but we do it
true /* force SQL99 rules */ );
partitionClause = transformGroupClause(pstate,
windef->partitionClause,
- false /* not GROUP BY ALL */ ,
NULL,
targetlist,
orderClause,
save_ingroupby = context->inGroupBy;
context->inGroupBy = true;
- if (query->groupByAll)
- appendStringInfoString(buf, "ALL");
- else if (query->groupingSets == NIL)
+ if (query->groupingSets == NIL)
{
sep = "";
foreach(l, query->groupClause)
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202607071
+#define CATALOG_VERSION_NO 202607172
#endif
List *groupClause; /* a list of SortGroupClause's */
bool groupDistinct; /* was GROUP BY DISTINCT used? */
- bool groupByAll; /* was GROUP BY ALL used? */
List *groupingSets; /* a list of GroupingSet's if present */
Node *whereClause; /* WHERE qualification */
List *groupClause; /* GROUP BY clauses */
bool groupDistinct; /* Is this GROUP BY DISTINCT? */
- bool groupByAll; /* Is this GROUP BY ALL? */
Node *havingClause; /* HAVING conditional-expression */
List *windowClause; /* WINDOW window_name AS (...), ... */
ParseExprKind exprKind, const char *constructName,
LimitOption limitOption);
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
- bool groupByAll,
List **groupingSets,
List **targetlist, List *sortClause,
ParseExprKind exprKind, bool useSQL99);
drop table t_having;
drop type avg_rec;
--
--- Test GROUP BY ALL
---
--- We don't care about the data here, just the proper transformation of the
--- GROUP BY clause, so test some queries and verify the EXPLAIN plans.
---
-CREATE TEMP TABLE t1 (
- a int,
- b int,
- c int
-);
--- basic example
-EXPLAIN (COSTS OFF) SELECT b, COUNT(*) FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: b
- -> Seq Scan on t1
-(3 rows)
-
--- multiple columns, non-consecutive order
-EXPLAIN (COSTS OFF) SELECT a, SUM(b), b FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: a, b
- -> Seq Scan on t1
-(3 rows)
-
--- multi columns, no aggregate
-EXPLAIN (COSTS OFF) SELECT a + b FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: (a + b)
- -> Seq Scan on t1
-(3 rows)
-
--- check we detect a non-top-level aggregate
-EXPLAIN (COSTS OFF) SELECT a, SUM(b) + 4 FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: a
- -> Seq Scan on t1
-(3 rows)
-
--- including grouped column is okay
-EXPLAIN (COSTS OFF) SELECT a, SUM(b) + a FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: a
- -> Seq Scan on t1
-(3 rows)
-
--- including non-grouped column, not so much
-EXPLAIN (COSTS OFF) SELECT a, SUM(b) + c FROM t1 GROUP BY ALL;
-ERROR: column "t1.c" must appear in the GROUP BY clause or be used in an aggregate function
-LINE 1: EXPLAIN (COSTS OFF) SELECT a, SUM(b) + c FROM t1 GROUP BY AL...
- ^
--- all aggregates, should reduce to GROUP BY ()
-EXPLAIN (COSTS OFF) SELECT COUNT(a), SUM(b) FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- Aggregate
- Group Key: ()
- -> Seq Scan on t1
-(3 rows)
-
--- likewise with empty target list
-EXPLAIN (COSTS OFF) SELECT FROM t1 GROUP BY ALL;
- QUERY PLAN
------------------------
- Result
- Replaces: Aggregate
-(2 rows)
-
--- window functions are not to be included in GROUP BY, either
-EXPLAIN (COSTS OFF) SELECT a, COUNT(a) OVER (PARTITION BY a) FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------------------
- WindowAgg
- Window: w1 AS (PARTITION BY a)
- -> Sort
- Sort Key: a
- -> HashAggregate
- Group Key: a
- -> Seq Scan on t1
-(7 rows)
-
--- all cols
-EXPLAIN (COSTS OFF) SELECT *, count(*) FROM t1 GROUP BY ALL;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: a, b, c
- -> Seq Scan on t1
-(3 rows)
-
--- group by all with grouping element(s) (equivalent to GROUP BY's
--- default behavior, explicit antithesis to GROUP BY DISTINCT)
-EXPLAIN (COSTS OFF) SELECT a, count(*) FROM t1 GROUP BY ALL a;
- QUERY PLAN
-----------------------
- HashAggregate
- Group Key: a
- -> Seq Scan on t1
-(3 rows)
-
--- verify deparsing of GROUP BY ALL
-CREATE TEMP VIEW v1 AS SELECT b, COUNT(*) FROM t1 GROUP BY ALL;
-SELECT pg_get_viewdef('v1'::regclass);
- pg_get_viewdef
------------------------
- SELECT b, +
- count(*) AS count+
- FROM t1 +
- GROUP BY ALL;
-(1 row)
-
-DROP VIEW v1;
-DROP TABLE t1;
---
-- Test GROUP BY matching of join columns that are type-coerced due to USING
--
create temp table t1(f1 int, f2 int);
drop table t_having;
drop type avg_rec;
---
--- Test GROUP BY ALL
---
--- We don't care about the data here, just the proper transformation of the
--- GROUP BY clause, so test some queries and verify the EXPLAIN plans.
---
-
-CREATE TEMP TABLE t1 (
- a int,
- b int,
- c int
-);
-
--- basic example
-EXPLAIN (COSTS OFF) SELECT b, COUNT(*) FROM t1 GROUP BY ALL;
-
--- multiple columns, non-consecutive order
-EXPLAIN (COSTS OFF) SELECT a, SUM(b), b FROM t1 GROUP BY ALL;
-
--- multi columns, no aggregate
-EXPLAIN (COSTS OFF) SELECT a + b FROM t1 GROUP BY ALL;
-
--- check we detect a non-top-level aggregate
-EXPLAIN (COSTS OFF) SELECT a, SUM(b) + 4 FROM t1 GROUP BY ALL;
-
--- including grouped column is okay
-EXPLAIN (COSTS OFF) SELECT a, SUM(b) + a FROM t1 GROUP BY ALL;
-
--- including non-grouped column, not so much
-EXPLAIN (COSTS OFF) SELECT a, SUM(b) + c FROM t1 GROUP BY ALL;
-
--- all aggregates, should reduce to GROUP BY ()
-EXPLAIN (COSTS OFF) SELECT COUNT(a), SUM(b) FROM t1 GROUP BY ALL;
-
--- likewise with empty target list
-EXPLAIN (COSTS OFF) SELECT FROM t1 GROUP BY ALL;
-
--- window functions are not to be included in GROUP BY, either
-EXPLAIN (COSTS OFF) SELECT a, COUNT(a) OVER (PARTITION BY a) FROM t1 GROUP BY ALL;
-
--- all cols
-EXPLAIN (COSTS OFF) SELECT *, count(*) FROM t1 GROUP BY ALL;
-
--- group by all with grouping element(s) (equivalent to GROUP BY's
--- default behavior, explicit antithesis to GROUP BY DISTINCT)
-EXPLAIN (COSTS OFF) SELECT a, count(*) FROM t1 GROUP BY ALL a;
-
--- verify deparsing of GROUP BY ALL
-CREATE TEMP VIEW v1 AS SELECT b, COUNT(*) FROM t1 GROUP BY ALL;
-SELECT pg_get_viewdef('v1'::regclass);
-
-DROP VIEW v1;
-DROP TABLE t1;
-
--
-- Test GROUP BY matching of join columns that are type-coerced due to USING
--