Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

GROUP BY & Aggregates
SQL

GROUP BY & Aggregates

Learn how to summarize data and perform calculations across sets of rows using aggregate functions.

Aggregate functions perform a calculation on a set of values and return a single value. They are often used with the GROUP BY clause.

Common Aggregate Functions

FunctionDescriptionNULL Handling
COUNT(*)Counts all rowsIncludes NULLs
COUNT(column)Counts non-NULL valuesExcludes NULLs
SUM(column)Total of a numeric columnIgnores NULLs
AVG(column)Average valueIgnores NULLs
MAX(column)Highest valueIgnores NULLs
MIN(column)Lowest valueIgnores NULLs

COUNT(*) vs COUNT(column)

This is a frequent interview topic:

  • COUNT(*) counts every row in the result set, including rows with NULL values.
  • COUNT(column_name) counts only the rows where the specified column is not NULL.
-- Total employees = 100, those with emails = 85
SELECT COUNT(*), COUNT(email) FROM employees;
-- Result: 100, 85

GROUP BY Basics

GROUP BY groups rows that have the same values into summary rows:

SELECT department_id, COUNT(*) AS employee_count
FROM employees
GROUP BY department_id;

GROUP BY Rules

Every column in the SELECT list must either:

  • Appear in the GROUP BY clause, or
  • Be wrapped in an aggregate function

Violating this rule causes an error in most databases (PostgreSQL, SQL Server, Oracle). MySQL is lenient but produces unpredictable results.

The HAVING Clause

WHERE filters rows before grouping. HAVING filters groups after grouping. You cannot use aggregate functions in WHERE.

SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 70000;

GROUP BY with Multiple Columns

SELECT department_id, job_role, COUNT(*) AS count
FROM employees
GROUP BY department_id, job_role;

GROUP BY with Expressions

You can group by computed values:

SELECT YEAR(hire_date) AS hire_year, COUNT(*) AS hired
FROM employees
GROUP BY YEAR(hire_date);

Note: In some databases, you must repeat the expression in GROUP BY — you cannot use the alias.

ROLLUP and CUBE

For subtotals and grand totals, databases support extensions like ROLLUP:

SELECT department_id, job_role, SUM(salary)
FROM employees
GROUP BY ROLLUP (department_id, job_role);

Q: Difference between COUNT(*) and COUNT(column)?

A:

  • COUNT(*) counts every row in the result set, including rows with NULL values.
  • COUNT(column_name) counts only the rows where the specified column is not NULL.

Q: What happens if you use non-grouped columns in SELECT?

A: In most SQL standard compliant databases (like PostgreSQL), you will get an error. Every column in the SELECT list must either be an aggregate function or be included in the GROUP BY clause.

Q: What is the purpose of the GROUP BY clause?

A: It groups rows that have the same values into summary rows, typically used with aggregate functions to find totals, averages, or counts for each group.

Q: Difference between WHERE and HAVING?

A: WHERE filters rows before grouping. HAVING filters groups after grouping. You cannot use aggregate functions in WHERE.

1. Count employees per department.

SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id;

2. Find departments with more than 5 employees.

SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 5;

3. Find highest salary per department.

SELECT department_id, MAX(salary)
FROM employees
GROUP BY department_id;

4. Find duplicate emails.

SELECT email, COUNT(email)
FROM users
GROUP BY email
HAVING COUNT(email) > 1;

5. Calculate running total of sales (Using Window Function).

SELECT sale_date, amount,
       SUM(amount) OVER (ORDER BY sale_date) as running_total
FROM sales;

6. Average salary per department with employee count.

SELECT department_id,
       COUNT(*) AS emp_count,
       ROUND(AVG(salary), 2) AS avg_salary
FROM employees
GROUP BY department_id;

My Private Notes

Notes are auto-saved locally to this device.