Home Functions AVG()
SQL Aggregate Function

AVG()

AVG() is an aggregate function that returns the mean (average) of the numeric values in a group.

MySQLPostgreSQLSQL ServerSQLite
Returns: A numeric value (the mean). Returns NULL when there are no non-NULL values.

Syntax

AVG(expression)AVG(DISTINCT expression)AVG(expression) OVER (...)
ParameterTypeRequiredDescription
expression numeric column or expression yes The values to average. NULLs are ignored.
DISTINCT expression numeric no Averages only the distinct values.

How it works

AVG() returns the arithmetic mean of the non-NULL values in a group. It is equivalent to SUM() divided by COUNT() of the same non-NULL values, and like the other aggregates it pairs with GROUP BY to give one average per group.

The key subtlety is that AVG ignores NULLs in both the total and the count. If you want NULLs to count as zero, do not use AVG directly: divide an explicit SUM by COUNT(*) instead, so the missing values still pull the average down.

Watch the data type. In some engines, averaging an integer column can perform integer division and truncate the result. Cast to a decimal type when you need fractional precision.

Examples

Average of a column

SELECT AVG(total) AS avg_order_value
FROM orders;
Result
avg_order_value
---------------
      184.28

Average per group

SELECT country, AVG(total) AS avg_order
FROM orders
GROUP BY country
ORDER BY avg_order DESC;
Result
country | avg_order
--------+----------
 UAE    |    201.7
 KSA    |    173.4

Force fractional precision

-- avg of an integer column, kept fractional
SELECT AVG(CAST(rating AS DECIMAL(5,2))) AS avg_rating
FROM reviews;
Result
avg_rating
----------
     4.37

Moving average as a window function

SELECT order_date, total,
       AVG(total) OVER (
         ORDER BY order_date
         ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
       ) AS moving_avg_3
FROM orders
ORDER BY order_date;
Result
order_date | total | moving_avg_3
-----------+-------+-------------
 2026-01-02|  120  |       120.0
 2026-01-03|   80  |       100.0
 2026-01-05|  260  |       153.3

Common mistakes

Wrong
-- NULL ratings are skipped, so this average is
-- only over the reviews that HAVE a rating
SELECT AVG(rating) FROM reviews;
Right
-- Treat missing ratings as 0 in the average
SELECT SUM(rating) * 1.0 / COUNT(*) FROM reviews;

AVG ignores NULLs in the denominator. If NULL should count as zero, divide an explicit SUM by COUNT(*) instead of using AVG.

Wrong
-- Integer division can truncate to a whole number
SELECT AVG(score) FROM games;   -- e.g. 7 instead of 7.5
Right
SELECT AVG(score * 1.0) FROM games;   -- 7.5

In several engines, averaging an integer column returns an integer. Multiply by 1.0 or CAST to DECIMAL to keep the fraction.

Performance

AVG() is a single pass, just like SUM and COUNT, so its cost tracks the number of rows scanned. Filter and index the columns in WHERE and GROUP BY to keep it fast; the average itself is negligible.

For a weighted average, compute it explicitly as SUM(value * weight) / SUM(weight) rather than AVG, which only gives an unweighted mean.

Interview questions

How does AVG() handle NULL values?

AVG ignores NULLs in both the sum and the count, so it averages only the rows that have a value. To treat NULL as zero, use SUM(x) / COUNT(*) instead.

Why might AVG() on an integer column return a whole number?

Some engines perform integer division and truncate the result. Cast the column to a decimal or multiply by 1.0 to get a fractional average.

SELECT AVG(score * 1.0) AS avg_score FROM games;

How would you compute a moving average of the last 3 rows?

Use AVG as a window function with a frame: AVG(total) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW).

How do you calculate a weighted average?

AVG only gives an unweighted mean. Compute a weighted average as SUM(value * weight) / SUM(weight).

Master SQL, one function at a time

Browse the full SQL functions library, or learn the fundamentals with our free, structured courses.