Remember, aggregate functions are those that work with multiple rows of data at once and return a single result. In PostgreSQL, you'll often use these aggregate functions:
SUM()— sums up data.AVG()— finds the average value.MIN()— finds the minimum value.MAX()— finds the maximum value.COUNT()— counts rows.
At first glance, it's simple: you pass a column or expression to the function and get a result. But what happens if the column has a NULL?
How NULL Behaves in Aggregates: Quick Overview
This is where things get interesting:
SUM()andAVG()ignoreNULL. If at least one record has aNULLvalue, it just doesn't get included in the calculation. Seems fair, right? How can the sum go up if someone "didn't show up to the party"? Or how do you calculate an average if you're missing a value?MIN()andMAX()also skipNULL. They find the minimum or maximum only among the data that's notNULL. So, if you're looking for the youngest employee and someone forgot to fill in their birth date,NULLwon't win the contest.COUNT(*)counts all rows, even those withNULL. ButCOUNT(column)only counts rows where the specified column has a value, soNULLgets ignored.
Let's break this down with some examples.
Examples of Using Aggregate Functions with NULL
Here's a students_scores table with students' test scores:
| student_id | name | score |
|---|---|---|
| 1 | Alice | 85 |
| 2 | Bob | NULL |
| 3 | Charlie | 92 |
| 4 | Dana | NULL |
| 5 | Elena | 74 |
Now let's run a few queries and see what happens:
- Total score:
SUM()
SELECT SUM(score) AS total_score
FROM students_scores;
Result:
| total_score |
|---|
| 251 |
As you can see, the missing NULL values just didn't get summed up. For Alice (85), Charlie (92), and Elena (74), the total is 251. Bob and Dana were left out.
- Average score:
AVG()
SELECT AVG(score) AS average_score
FROM students_scores;
Result:
| average_score |
|---|
| 83.67 |
Again, NULL was ignored, and the average was calculated only for those who had scores: (85 + 92 + 74) / 3 = 83.67.
- Minimum and maximum score:
MIN()andMAX()
SELECT
MIN(score) AS min_score,
MAX(score) AS max_score
FROM students_scores;
Result:
| min_score | max_score |
|---|---|
| 74 | 92 |
Here too, it's simple: NULL values were ignored again, so the minimum is 74 and the maximum is 92.
- Counting rows:
COUNT(*)vsCOUNT(column)
SELECT
COUNT(*) AS total_rows,
COUNT(score) AS non_null_scores
FROM students_scores;
Result:
| total_rows | non_null_scores |
|---|---|
| 5 | 3 |
COUNT(*)counted all rows, including those wherescoreisNULL.COUNT(score)only counted rows where thescorecolumn has a value.
Practical Cases
Let's look at a few practical examples.
Example 1: Counting Employees With and Without Salary
Suppose we have an employees table with salaries.
| id | name | salary |
|---|---|---|
| 1 | Alex Lin | 50000 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 60000 |
| 4 | Otto Art | NULL |
| 5 | Liam Park | 55000 |
We want to know how many employees have a salary listed and how many don't.
SELECT
COUNT(*) AS total_employees,
COUNT(salary) AS employees_with_salary,
COUNT(*) - COUNT(salary) AS employees_without_salary
FROM employees;
Here's what's happening:
COUNT(*)returns the total number of employees.COUNT(salary)counts how many employees have a salary listed.- To get the number of employees without a salary, we just subtract one value from the other.
Result
| total_employees | employees_with_salary | employees_without_salary |
|---|---|---|
| 5 | 3 | 2 |
Example 2: Calculating Average Product Price with Missing Data
You're the owner of a magic shop, and the products table has a price column, but some products don't have a price yet.
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 150 |
| 2 | Enchanted Cloak | NULL |
| 3 | Potion Bottle | 75 |
| 4 | Spell Book | 200 |
| 5 | Crystal Ball | NULL |
You need to find the average price only for products where it's set.
SELECT AVG(price) AS average_price
FROM products;
Result:
| average_price |
|---|
| 141.6667 |
If you want to set a default price for products without a price (like, make it 0), you can use the COALESCE() function from the next lecture.
Example 3: Finding the Youngest and Oldest Student
The students table stores students' ages, but for some of them the age is unknown (NULL).
| id | name | age |
|---|---|---|
| 1 | Alex Lin | 20 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 19 |
| 4 | Otto Art | 22 |
| 5 | Liam Park | NULL |
We want to find the youngest and oldest student.
SELECT
MIN(age) AS youngest_student,
MAX(age) AS eldest_student
FROM students;
Result:
| youngest_student | eldest_student |
|---|---|
| 19 | 22 |
This query will return the minimum and maximum age only for students whose age is set. NULL gets skipped again.
Things to Watch Out For
When you're working with NULL in aggregates, keep these things in mind:
- In
SUM()andAVG(),NULLis ignored. You can use this to avoid adding "empty" values to your calculations. - If you need to count rows with
NULLin a column, useCOUNT(*). - When using
MIN()orMAX(),NULLdoesn't affect the result. But if the whole column is justNULL, the result will beNULLtoo.
Tips for Working with NULL
- Think about your use case. It's important to know if you need to include
NULLin your query. Sometimes, like withAVG(), ignoring them is exactly what you want. Other times, like when counting total rows, you want to include rows withNULLtoo. - Use
COALESCE()if needed. If you want to replaceNULLwith a default value in your calculations, theCOALESCE()function is your friend (but that's for the next lecture). - Don't mix up
COUNT(*)andCOUNT(column). This is a classic rookie mistake. The first counts all rows, the second only counts rows with non-null values.
Now you know how the sneaky, silent NULL can mess with your aggregates. This knowledge will help you avoid nasty surprises and use NULL to your advantage. In the next lecture, we'll check out the powerful COALESCE() tool to handle NULL even better.
GO TO FULL VERSION