SQL Aggregate Functions Explained (COUNT, SUM, AVG)
New to SQL? Learn aggregate functions COUNT, SUM, AVG, MIN, and MAX with simple, copy-pasteable examples every beginner can follow today.
New to SQL? Learn aggregate functions COUNT, SUM, AVG, MIN, and MAX with simple, copy-pasteable examples every beginner can follow today.
SQL aggregate functions are built-in tools that take many rows of data and turn them into one summary number. The five you will use almost every day are COUNT(), SUM(), AVG(), MIN(), and MAX(). For example, COUNT() tells you how many rows match, and SUM() adds up a column of numbers. In this guide you will learn what each one does, with small, copy-pasteable examples you can run yourself.
A table in a database is just rows and columns, like a spreadsheet. Normally, a SELECT query returns one row of output for every row that matches. An aggregate function is different: it looks at a whole column (or a whole group of rows) and returns a single value that summarizes them, such as a total, an average, or a count.
Think of a spreadsheet of orders. Instead of scrolling through 5,000 rows to find the total sales, you can ask the database to add them up for you in one line of SQL.
| Function | What it does | Example |
|---|---|---|
| COUNT() | Counts how many rows match | COUNT(*) counts every row |
| SUM() | Adds up all the values in a column | SUM(price) totals a price column |
| AVG() | Calculates the average value | AVG(price) gives the mean price |
| MIN() | Finds the smallest value | MIN(price) finds the cheapest item |
| MAX() | Finds the largest value | MAX(price) finds the most expensive item |
We will use one sample table called orders for every example below. It has three columns: id (a unique number for each order), customer (the customer's name), and amount (how much the order cost, in dollars).
COUNT() answers the question "how many?". It is the aggregate function beginners reach for most often, because almost every report starts with a total number of something.
SELECT COUNT(*) AS total_orders
FROM orders;Line by line: SELECT COUNT(*) tells the database to count every row, no matter what is in it. AS total_orders just renames the result column so the output is easy to read (without it, the column would be called count, which is fine too). FROM orders says which table to look in. The result is a single row with one number, like 248.
You can also count a specific column instead of every row. COUNT(customer) counts only the rows where customer is not empty (in SQL, an empty or unknown value is called NULL). COUNT(*) counts all rows, including ones with NULL values; COUNT(column_name) skips NULLs in that column.
SUM() adds every value in a numeric column together and returns one total. It only works on numbers, not on text.
SELECT SUM(amount) AS total_revenue
FROM orders;This adds up every value in the amount column across all rows in the orders table and labels the single result total_revenue. If your orders table has rows of 20, 35, and 45, this query returns 100. Rows where amount is NULL are treated as if they were not there, so they do not lower the total.
AVG() calculates the mean of a column: it adds up all the values, then divides by how many values there are (ignoring NULLs).
SELECT AVG(amount) AS average_order_value
FROM orders;AVG(amount) computes the average of every value in the amount column. For orders of 20, 35, and 45, AVG returns 33.33 (100 divided by 3). This is useful for questions like "what is our average order value?" without you having to calculate it by hand.
MIN() returns the smallest value in a column, and MAX() returns the largest. They work on numbers, dates, and even text (alphabetically), which makes them useful far beyond just prices.
SELECT MIN(amount) AS cheapest_order,
MAX(amount) AS most_expensive_order
FROM orders;This query runs two aggregate functions in the same SELECT statement, separated by a comma. MIN(amount) finds the lowest value in the amount column, and MAX(amount) finds the highest. The result is one row with two columns, showing both numbers side by side.
So far every example summarized the entire table into one number. GROUP BY lets you get one summary number per group instead — for example, total spend per customer rather than one grand total for everyone.
SELECT customer, SUM(amount) AS total_spent
FROM orders
GROUP BY customer;GROUP BY customer tells the database to split the orders table into one group per unique customer name, before running SUM(amount) inside each group. The result is one row per customer, showing how much that customer spent in total. Any column you SELECT alongside an aggregate function (like customer here) must also appear in the GROUP BY clause.
Want a deeper look at how GROUP BY works, including grouping by multiple columns?
Read the SQL GROUP BY guideWHERE filters rows before they are grouped. But if you want to filter after the aggregate function has run — for example, only show customers who spent more than $100 in total — you need HAVING instead.
SELECT customer, SUM(amount) AS total_spent
FROM orders
GROUP BY customer
HAVING SUM(amount) > 100;This runs the same grouped SUM as before, but HAVING SUM(amount) > 100 removes any customer group whose total is 100 or less from the final result. You cannot write WHERE SUM(amount) > 100 here, because WHERE runs before grouping happens, and the total does not exist yet at that point.
Aggregate functions are built-in SQL commands that take many rows of data and return a single summary value, such as a count, total, average, minimum, or maximum. The most common ones are COUNT(), SUM(), AVG(), MIN(), and MAX().
COUNT(*) counts every row in the result, no matter what values it contains. COUNT(column_name) counts only the rows where that specific column is not NULL (not empty). If a column has some missing values, the two counts can be different.
No. SUM() and AVG() both ignore NULL values completely rather than treating them as zero. AVG() also excludes NULL rows from the count it divides by, so the average only reflects rows that actually have a value.
Yes. You can list several aggregate functions in one SELECT statement, separated by commas, such as SELECT COUNT(*), SUM(amount), AVG(amount) FROM orders. The database calculates all of them in a single pass over the table.
Most databases require every non-aggregated column in your SELECT list to also appear in the GROUP BY clause. This is because the database needs to know how to group rows before it can decide what value to show for that column in each summary row.
Building an app or dashboard that needs solid database queries under the hood?
Talk to Fepiq about your projectOccasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.
Keep reading
Learn SQL CASE WHEN step by step. This beginner guide shows how to label, group and sort data with simple examples you can copy and run today.
PostgreSQL ILIKE explained for beginners: search text without worrying about case, learn % and _ wildcards, and copy simple examples you can run today.
Let's build something
Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.