SQL course menu
SQL GROUP BY Explained Simply (With Examples)
Learn how SQL GROUP BY turns many rows into summary rows. Count, sum, and average per group, group by multiple columns, filter groups with HAVING, and avoid common errors.
On this page
So far every query has returned one result row for each table row. But many real questions are about totals: How many employees are in each department? What is the average salary per city?
GROUP BY answers exactly these questions. It collects rows that share a value into groups, then calculates one summary row per group.
The example table#
First, aggregate functions#
GROUP BY works together with aggregate functions. An aggregate function takes many values and returns a single value:
Without GROUP BY, an aggregate treats the whole table as one big group:
SELECT COUNT(*) AS employees, AVG(salary) AS avg_salary
FROM employees;employees | avg_salary
----------+-----------
5 | 63600GROUP BY syntax#
SELECT group_column, AGGREGATE(other_column)
FROM table_name
GROUP BY group_column;Count rows per group#
How many employees work in each department?
SELECT department, COUNT(*) AS employees
FROM employees
GROUP BY department;department | employees
------------+----------
Engineering | 2
Marketing | 1
Sales | 2Here is what the database does:
- It sorts the five rows into piles, one pile per department: Sales (Asha, Priya), Engineering (Ravi, Meera), Marketing (John).
- For each pile it runs
COUNT(*). - It returns one row per pile.
Several aggregates at once#
You can calculate as many summaries as you like for each group:
SELECT department,
COUNT(*) AS employees,
SUM(salary) AS total_salary,
AVG(salary) AS avg_salary,
MAX(salary) AS highest_salary
FROM employees
GROUP BY department
ORDER BY total_salary DESC;department | employees | total_salary | avg_salary | highest_salary
------------+-----------+--------------+------------+---------------
Engineering | 2 | 159000 | 79500 | 81000
Sales | 2 | 113000 | 56500 | 61000
Marketing | 1 | 46000 | 46000 | 46000Group by more than one column#
When you group by two columns, each unique combination becomes a group:
SELECT department, city, COUNT(*) AS employees
FROM employees
GROUP BY department, city
ORDER BY department, city;department | city | employees
------------+-----------+----------
Engineering | Bengaluru | 1
Engineering | Pune | 1
Marketing | Mumbai | 1
Sales | Delhi | 1
Sales | Mumbai | 1Filter rows first with WHERE#
WHERE runs before grouping, so it decides which rows go into the piles at all:
SELECT department, COUNT(*) AS high_earners
FROM employees
WHERE salary > 55000
GROUP BY department;department | high_earners
------------+-------------
Engineering | 2
Sales | 1Asha (52,000) and John (46,000) were removed before grouping, so Marketing disappears completely and Sales only counts Priya.
Filter groups with HAVING#
What if you want to filter on the result of an aggregate, such as "departments with more than one employee"? WHERE cannot do this, because when WHERE runs, the groups do not exist yet. Use HAVING, which runs after grouping:
SELECT department, COUNT(*) AS employees
FROM employees
GROUP BY department
HAVING COUNT(*) > 1;department | employees
------------+----------
Engineering | 2
Sales | 2The order SQL runs a grouped query#
SELECT department, AVG(salary) AS avg_salary -- 5. pick columns
FROM employees -- 1. take the table
WHERE city <> 'Delhi' -- 2. remove rows
GROUP BY department -- 3. make groups
HAVING AVG(salary) > 50000 -- 4. remove groups
ORDER BY avg_salary DESC; -- 6. sort the resultRemembering this order explains most GROUP BY errors. For example, it is why you can use the alias avg_salary in ORDER BY (step 6 comes after SELECT) but most databases will not let you use it in HAVING (step 4 comes before).
Common mistakes#
Practice#
Practice exercise
Write a query that shows each city with the number of employees and the average salary, but only for cities that have at least two employees.
Show hint
Group by city, calculate COUNT(*) and AVG(salary), then use HAVING to keep groups with a count of 2 or more.
Show solution
SELECT city,
COUNT(*) AS employees,
AVG(salary) AS avg_salary
FROM employees
GROUP BY city
HAVING COUNT(*) >= 2;city | employees | avg_salary
-------+-----------+-----------
Mumbai | 2 | 49000Only Mumbai has two employees (Asha and John), and their average salary is (52,000 + 46,000) / 2 = 49,000.
Key takeaway#
Next, learn how to combine data from two tables with INNER JOIN.
Finished this lesson?