An aggregate function summarizes the rows of a group into a single value.
When an aggregate function is used with
the OVER clause, it becomes a window function, which computes values over a
group of rows and then returns a single result for each row.
Aggregate function call syntax
function_name(
[ DISTINCT ]
function_arguments
[ { IGNORE | RESPECT } NULLS ]
[ { HAVING { MAX | MIN } having_expression | GROUP BY grouping_expression [, ... ] } ]
[ ORDER BY key [ { ASC | DESC } ] [, ... ] ]
[ LIMIT n ]
)
[ OVER over_clause ]
Description
Each aggregate function supports all or a subset of the aggregate function call syntax. To build an aggregate function, use the following syntax:
DISTINCT: Aggregate each distinct value of an expression only once into the result.function_arguments: Specify the input values, columns, or expressions that the aggregate function evaluates and summarizes across the rows of a group.IGNORE NULLSorRESPECT NULLS: IfIGNORE NULLSis specified, theNULLvalues are excluded from the result. IfRESPECT NULLSis specified, bothNULLand non-NULLvalues can be included in the result.If neither
IGNORE NULLSnorRESPECT NULLSis specified, most functions default toIGNORE NULLSbehavior but in a few casesNULLvalues are respected.HAVING MAXorHAVING MIN: Restricts the set of rows that the function aggregates by a maximum or minimum value. For details, see HAVING MAX and HAVING MIN clause.GROUP BY: Performs an additional grouping on input rows to the aggregate function. Used to define multi-level aggregates.ORDER BY: Specifies the order of the values.For each sort key, the default sort direction is
ASC.NULLis the minimum possible value, soNULLs appear first inASCsorts and last inDESCsorts.If you're using floating point data types, see Floating point semantics on ordering and grouping.
The
ORDER BYclause is supported only for aggregate functions that depend on the order of their input. For those functions, if theORDER BYclause is omitted, the output is nondeterministic.This
ORDER BYclause can't be used if theOVERclause is used.If
DISTINCTis also specified, then the sort key must be the same asexpression.
LIMIT: Specifies the maximum number ofexpressioninputs in the result.If the input is an
ARRAYvalue, the limit applies to the number of input arrays, not the number of elements in the arrays. An empty array counts as1. ANULLarray isn't counted.If the input is a
STRINGvalue, the limit applies to the number of input strings, not the number of characters or bytes in the inputs. An empty string counts as1. ANULLstring isn't counted.The limit
nmust be a constantINT64.
OVER: If the aggregate function is also a window function, use this clause to define a window of rows around the row being evaluated. For each row, the aggregate function result is computed using the selected window of rows as input. If theOVERclause is used, aggregate function clauses, such asDISTINCT, aren't supported, but function call modifiers, such asIGNORE_NULLS, are still supported. To learn more about theOVERclause, see Window function calls.
Details
The clauses in an aggregate function call are applied in the following order:
OVERHAVING MAX/HAVING MINorGROUP BYIGNORE NULLSorRESPECT NULLSDISTINCTORDER BYLIMIT
When used in conjunction with a GROUP BY clause, the groups summarized
typically have at least one row. When the associated SELECT statement has
no GROUP BY clause or when certain aggregate function modifiers filter rows
from the group to be summarized, it's possible that the aggregate function
needs to summarize an empty group.
Restrict aggregation by a maximum or minimum value
Some aggregate functions support two optional clauses that are called
HAVING MAX and HAVING MIN. These clauses restrict the set of rows that a
function aggregates to rows that have a maximum or minimum value in a particular
column.
HAVING MAX clause
HAVING MAX having_expression
HAVING MAX restricts the set of input rows that the function aggregates to
only those with the maximum having_expression value. The maximum value is
computed as the result of MAX(having_expression) across rows in the group.
Only rows whose having_expression value is equal to this maximum value (using
SQL equality semantics) are included in the aggregation. All other rows are
ignored in the aggregation.
This clause supports all orderable data types,
except for ARRAY.
Examples
In the following query, rows with the most inches of precipitation, 4, are
added to a group, and then the year for one of these rows is produced.
Which row is produced is nondeterministic, not random.
WITH
Precipitation AS (
SELECT 2009 AS year, 'spring' AS season, 3 AS inches
UNION ALL
SELECT 2001, 'winter', 4
UNION ALL
SELECT 2003, 'fall', 1
UNION ALL
SELECT 2002, 'spring', 4
UNION ALL
SELECT 2005, 'summer', 1
)
SELECT ANY_VALUE(year HAVING MAX inches) AS any_year_with_max_inches FROM Precipitation;
/*--------------------------+
| any_year_with_max_inches |
+--------------------------+
| 2001 |
+--------------------------*/
HAVING MIN clause
HAVING MIN having_expression
HAVING MIN restricts the set of input rows that the function aggregates to
only those with the minimum having_expression value. The minimum value is
computed as the result of MIN(having_expression) across rows in the group.
Only rows whose having_expression value is equal to this minimum value (using
SQL equality semantics) are included in the aggregation. All other rows are
ignored in the aggregation.
This clause supports all orderable data types,
except for ARRAY.
Examples
In the following query, rows with the fewest inches of precipitation, 1,
are added to a group, and then the year for one of these rows is produced.
Which row is produced is nondeterministic, not random.
WITH
Precipitation AS (
SELECT 2009 AS year, 'spring' AS season, 3 AS inches
UNION ALL
SELECT 2001, 'winter', 4
UNION ALL
SELECT 2003, 'fall', 1
UNION ALL
SELECT 2002, 'spring', 4
UNION ALL
SELECT 2005, 'summer', 1
)
SELECT ANY_VALUE(year HAVING MIN inches) AS any_year_with_min_inches FROM Precipitation;
/*--------------------------+
| any_year_with_min_inches |
+--------------------------+
| 2003 |
+--------------------------*/
Aggregate function examples
A simple aggregate function call for COUNT, MIN, and MAX looks like this:
SELECT
COUNT(*) AS total_count,
COUNT(fruit) AS non_null_count,
MIN(fruit) AS min,
MAX(fruit) AS max
FROM
(
SELECT NULL AS fruit
UNION ALL
SELECT 'apple' AS fruit
UNION ALL
SELECT 'pear' AS fruit
UNION ALL
SELECT 'orange' AS fruit
)
/*-------------+----------------+-------+------+
| total_count | non_null_count | min | max |
+-------------+----------------+-------+------+
| 4 | 3 | apple | pear |
+-------------+----------------+-------+------*/
In the following example, the average of x over a specified window is returned
for each row. To learn more about windows and how to use them, see
Window function calls.
SELECT
x,
AVG(x) OVER (ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS avg
FROM UNNEST([0, 2, 4, 4, 5]) AS x;
/*------+------+
| x | avg |
+------+------+
| 0 | 0 |
| 2 | 1 |
| 4 | 3 |
| 4 | 4 |
| 5 | 4.5 |
+------+------*/
Multi-level aggregation
Standard SQL doesn't allow an aggregate function to have other aggregate functions as arguments. As a result, expressing multi-stage aggregation typically requires using a subquery.
For example, say you have a table of sales data and want to calculate the average daily sales by averaging the sums of the rows. You can do this with a subquery:
WITH Sales AS (
SELECT 'Apples' AS Product, 100 AS revenue, TIMESTAMP '2026-01-01 10:00:00' AS time UNION ALL
SELECT 'Apples', 150, TIMESTAMP '2026-01-01 12:00:00' UNION ALL
SELECT 'Apples', 200, TIMESTAMP '2026-01-02 10:00:00' UNION ALL
SELECT 'Oranges', 50, TIMESTAMP '2026-01-01 10:00:00' UNION ALL
SELECT 'Oranges', 60, TIMESTAMP '2026-01-02 10:00:00' UNION ALL
SELECT 'Oranges', 70, TIMESTAMP '2026-01-02 12:00:00'
)
SELECT
Product,
AVG(daily_sales) AS avg_daily_sales
FROM
(
SELECT
Product,
SUM(revenue) AS daily_sales
FROM Sales
GROUP BY Product, DATE(time)
)
GROUP BY Product
ORDER BY Product;
/*---------+-----------------+
| Product | avg_daily_sales |
+---------+-----------------+
| Apples | 225 |
| Oranges | 90 |
+---------+-----------------*/
Multi-level aggregate syntax removes this restriction by allowing you to add
an aggregate function as an argument to another aggregate function, when the
outer aggregate function has its own GROUP BY clause. Using multi-level
aggregation, the previous query can be simplified to the following:
WITH Sales AS (
SELECT 'Apples' AS Product, 100 AS revenue, TIMESTAMP '2026-01-01 10:00:00' AS time UNION ALL
SELECT 'Apples', 150, TIMESTAMP '2026-01-01 12:00:00' UNION ALL
SELECT 'Apples', 200, TIMESTAMP '2026-01-02 10:00:00' UNION ALL
SELECT 'Oranges', 50, TIMESTAMP '2026-01-01 10:00:00' UNION ALL
SELECT 'Oranges', 60, TIMESTAMP '2026-01-02 10:00:00' UNION ALL
SELECT 'Oranges', 70, TIMESTAMP '2026-01-02 12:00:00'
)
SELECT
Product,
AVG(SUM(revenue) GROUP BY DATE(time)) AS avg_daily_sales
FROM Sales
GROUP BY Product
ORDER BY Product;
/*---------+-----------------+
| Product | avg_daily_sales |
+---------+-----------------+
| Apples | 225 |
| Oranges | 90 |
+---------+-----------------*/
When an aggregate function has a GROUP BY clause, it becomes a multi-level
aggregate function. Multi-level aggregate functions work by first grouping the
input rows based the GROUP BY modifier on the aggregate function, and then
evaluating the inner aggregate function arguments over those groups. The
intermediate inner aggregation results are then passed to the enclosing
aggregate function.
In the previous example, the SUM(revenue) aggregation is effectively grouped
by both Product and DATE(time) to calculate an intermediate aggregation
result. This intermediate result is then averaged while grouping by Product to
get the final result per product.
Multi-level aggregation rules and constraints
The following rules and constraints apply to multi-level aggregation:
- Multi-level aggregation is supported in only function arguments, the
DISTINCTclause, and theGROUP BYmodifier within the aggregate function call. - You can't use the
HAVING MINorHAVING MAXclause in addition to aGROUP BYclause in a multi-level aggregation function. You also can't use the following clauses in a multi-aggregation function:
ORDER BYLIMITIGNORE NULLSorRESPECT NULLS
You can't use multi-level aggregate functions in the
PIVOToperator.You can't use a
GROUPINGfunction within a multi-level aggregate body, or withGROUP BYmodifiers. For example, the following expressions result in an error:SUM(GROUPING(...) GROUP BY Y) -- Error GROUPING(... GROUP BY Y) -- ErrorYou can't use multi-level aggregation with the differential privacy clause.
You can't use multi-level aggregation with the aggregation threshold clause.
You can't use grouping keys with collation in a multi-level aggregation.
You can't use multi-level aggregation with continuous queries.
You can't use an empty aggregate function list in the inner aggregation of a multi-level aggregation (for example,
COUNT(* GROUP BY field1)).You can't use more than two nested aggregate functions in a multi-level aggregation:
SUM(AVG(MIN(X) GROUP BY Y) GROUP BY Z) -- Error; 3 nested aggregate functions.
Avoid overcounting with multi-level aggregation
Aggregating over the result of a JOIN operation can result in overcounting
of the aggregated result. Consider the following query which attempts to
calculate the average salary of employees with at least one dependent child:
WITH Employees AS (
SELECT 1 AS empno, 150000 AS salary UNION ALL
SELECT 2, 100000 UNION ALL
SELECT 3, 80000
),
Dependents AS (
SELECT 1 AS empno, 'Child' AS relationship UNION ALL
SELECT 2, 'Child' UNION ALL
SELECT 2, 'Child' UNION ALL
SELECT 3, 'Child' UNION ALL
SELECT 3, 'Child'
)
SELECT
empno,
salary,
relationship
FROM Employees
INNER JOIN Dependents
USING (empno)
WHERE relationship = 'Child'
ORDER BY empno;
/*-------+--------+--------------+
| empno | salary | relationship |
+-------+--------+--------------+
| 1 | 150000 | Child |
| 2 | 100000 | Child |
| 2 | 100000 | Child |
| 3 | 80000 | Child |
| 3 | 80000 | Child |
+-------+--------+--------------*/
The issue is that the INNER JOIN operation results in a table where the salary
for a given employee appears more than once if they have more than
one child listed in the Dependents table. For example, employees 2 and 3 are
repeated for each dependent child. Taking the average (AVG) of salary on this
table overcounts those two salaries, leading to an incorrect result.
Overcounting like in the previous example can be avoided with multi-level
aggregation. The following revised version of the previous query uses
multi-level aggregation with an ANY_VALUE function to get the correct average
without overcounting:
WITH Employees AS (
SELECT 1 AS empno, 150000 AS salary UNION ALL
SELECT 2, 100000 UNION ALL
SELECT 3, 80000
),
Dependents AS (
SELECT 1 AS empno, 'Child' AS relationship UNION ALL
SELECT 2, 'Child' UNION ALL
SELECT 2, 'Child' UNION ALL
SELECT 3, 'Child' UNION ALL
SELECT 3, 'Child'
)
SELECT
AVG(ANY_VALUE(salary) GROUP BY empno) AS avg_salary
FROM Employees
INNER JOIN Dependents
USING (empno)
WHERE relationship = 'Child'
-- Results of the ANY_VALUE intermediate aggregate:
/*-------+-------------------+
| empno | ANY_VALUE(salary) |
+-------+-------------------+
| 1 | 150000 |
| 2 | 100000 |
| 3 | 80000 |
+-------+-------------------*/
-- Final result:
/*------------+
| avg_salary |
+------------+
| 110000 |
+------------*/