Skip to main content

Aggregate Functions

Aggregate functions compute a single result from a set of input rows. They are typically used with the GROUP BY clause in SELECT statements, but can also be used without GROUP BY to aggregate over all rows. When used in a SELECT with non-aggregate columns and no GROUP BY, the result is a single row. All standard aggregate functions ignore NULL values (except count(*)). If every input value is NULL, the aggregate returns NULL, with the exception of count() (which returns 0) and total() (which returns 0.0).

Function Reference

Detailed Descriptions and Examples

The examples below use the following table:

avg(X)

Returns the average of all non-NULL values of X as a REAL (floating-point) number. Returns NULL if all values are NULL. Return type: REAL
avg(X) ignores NULL values in both the sum and the count. In the example above, the South region has three non-NULL amounts (150 + 175 = 325, but also the NULL row is excluded), so the average is computed over the non-NULL values only.

count(X) and count(*)

count(X) returns the number of rows where X is not NULL. count(*) returns the total number of rows in the group, including rows with NULL values. Return type: INTEGER

group_concat(X) and group_concat(X, Y)

Concatenates all non-NULL values of X into a single string. The default separator is a comma (,). When Y is provided, it is used as the separator instead. Return type: TEXT

string_agg(X, Y)

Alias for group_concat(X, Y). Provided for compatibility with PostgreSQL. Return type: TEXT

max(X) and min(X)

max(X) returns the maximum non-NULL value of X. min(X) returns the minimum non-NULL value of X. Values are compared using the standard SQLite comparison rules. Returns NULL if all values are NULL. Return type: Same as the input type
When max(X) or min(X) is called with a single argument in an aggregate context, it acts as an aggregate function. When called with two or more arguments (e.g., max(a, b, c)), it acts as a scalar function and returns the largest argument.

sum(X) and total(X)

Both functions return the sum of all non-NULL values of X. They differ in return type and behavior when all values are NULL. Return type:
  • sum(X): INTEGER if all non-NULL inputs are integers and no overflow occurs, otherwise REAL. Returns NULL if all values are NULL.
  • total(X): Always REAL. Returns 0.0 if all values are NULL.

Difference between sum() and total()

The key difference appears when all values in the group are NULL:
This makes total() convenient when you need a numeric result even for empty or all-NULL groups:
sum(X) returns an integer result when all inputs are integers and the result fits within a 64-bit signed integer. If the sum overflows, it automatically switches to REAL. Use total(X) when you always want a floating-point result.

Using Aggregates with GROUP BY

The GROUP BY clause partitions rows into groups. Each aggregate function is computed independently for each group.

Filtering Groups with HAVING

The HAVING clause filters groups after aggregation. Use WHERE to filter rows before aggregation and HAVING to filter groups after.

Aggregates with DISTINCT

The DISTINCT keyword causes the aggregate to consider only unique non-NULL values.

Aggregates as Window Functions

All standard aggregate functions can be used as window functions. When used with an OVER clause, the function computes a running or partitioned result without collapsing rows.

Ordered-Set Aggregates (WITHIN GROUP)

mode, percentile_cont and percentile_disc with WITHIN GROUP (ORDER BY ...) are built in and available by default — no extension required. The syntax and results match PostgreSQL. (Standard SQLite does not support WITHIN GROUP.)
Ordered-set aggregates compute a result over the values of an ORDER BY expression, sorted within each group:
The ORDER BY expression is the value being aggregated. For the percentile functions, the argument before WITHIN GROUP is the percentile fraction. NULL values of the ORDER BY expression are ignored; if there are no non-NULL values, the result is NULL.

mode()

Returns the most frequent value of X. If several values are equally frequent, the smallest is returned. Works with any type and returns the value in its original type. Return type: same as X
mode() is only valid with WITHIN GROUP; calling mode(X) without it is an error.

percentile_cont(fraction) and percentile_disc(fraction)

Compute the fraction-th percentile of X, where fraction is between 0.0 and 1.0:
  • percentile_cont returns a continuous, interpolated value (always REAL).
  • percentile_disc returns the actual element at the discrete percentile position, in its original type.
Return type: REAL for percentile_cont; same as X for percentile_disc
The fraction must be a constant with respect to the rows being aggregated — a literal, a constant expression, or a parameter. It cannot reference the columns being aggregated, and an out-of-range constant fraction is reported regardless of how many rows match:

Collation

Text values are ordered using the applicable collation — an explicit COLLATE on the ORDER BY expression, the column’s declared collation, or BINARY by default — consistent with ORDER BY.

Notes and limitations

  • A single ORDER BY expression is required. Multiple expressions, DESC, and NULLS FIRST/LAST inside WITHIN GROUP are not yet supported.
  • A subquery as the fraction argument is not currently supported.
  • These aggregates work with GROUP BY, FILTER (WHERE ...), HAVING, subqueries, CTEs, joins, and attached databases.
  • The percentile extension also provides non-standard two-argument forms, percentile_cont(Y, P) and percentile_disc(Y, P) (see Extension Aggregate Functions). The WITHIN GROUP forms above are the SQL-standard versions and require no extension.

Turso Extension: array_agg(X)

array_agg(X) is a Turso extension and is not part of standard SQLite. It is available by default in Turso without loading any additional extensions.
Collects all values of X (including NULLs) into an array. Returns NULL if the group is empty. Return type: BLOB (array)
For more array functions, see Array Functions.

Turso Extension: stddev(X)

stddev(X) is a Turso extension and is not part of standard SQLite. It is available by default in Turso without loading any additional extensions.
Returns the population standard deviation of all non-NULL values of X. Returns NULL if there are no non-NULL values. Return type: REAL

Extension Aggregate Functions

The following aggregate functions are available through the percentile extension. Load it before use.
These functions require the percentile extension. Load it with SELECT load_extension('./percentile'); or by configuring your connection to auto-load it.

median(X)

Returns the median (middle value) of all non-NULL values of X. Return type: REAL

percentile(Y, P)

Returns the P-th percentile of all non-NULL values of Y. Uses linear interpolation between adjacent values. Return type: REAL

percentile_cont(Y, P) and percentile_disc(Y, P)

These two-argument forms are a convenience provided by the percentile extension. The SQL-standard spelling — percentile_cont(P) WITHIN GROUP (ORDER BY Y) — is built in and needs no extension; see Ordered-Set Aggregates above. percentile_cont uses a continuous (interpolated) distribution, while percentile_disc returns a discrete input value. Return type: REAL
Note the difference in P range: percentile(Y, P) takes P from 0 to 100, while percentile_cont and percentile_disc take P from 0.0 to 1.0.

See Also