R
Rishtaara
SQL: Zero to Data Analyst
Lesson 11 of 30Article16 min

GROUP BY & HAVING

GROUP BY splits rows into groups — by course_id, city, or grade — then aggregates run on each group.

GROUP BY — one row per group

GROUP BY splits rows into groups — by course_id, city, or grade — then aggregates run on each group.

Every selected column must be in GROUP BY or inside an aggregate function.

Real-life example: GROUP BY is sorting exam papers into piles by class, then writing one average score per pile.

Average score per course
SELECT
  c.course_name,
  COUNT(e.enrollment_id) AS student_count,
  AVG(e.score) AS avg_score
FROM enrollments e
JOIN courses c ON e.course_id = c.course_id
GROUP BY c.course_name;

HAVING — filter groups after aggregation

WHERE filters rows before grouping. HAVING filters groups after GROUP BY — use it with aggregates like AVG(score) > 80.

Real-life example: HAVING is 'only show classes where average score is above 75' — you need the average first, then filter.

Courses with average score above 80
SELECT
  c.course_name,
  AVG(e.score) AS avg_score
FROM enrollments e
JOIN courses c ON e.course_id = c.course_id
GROUP BY c.course_name
HAVING AVG(e.score) > 80;