Lesson 3 of 4

Progress: 0/3 exercises solved (0%)

← Previous

Solve all exercises below to unlock the next lesson.

HAVING — filter on aggregates

Lesson Description

WHERE filters rows; HAVING filters groups.

Easy Project

Mini project: Create a hiring shortlist query that filters by role, salary range, and hire date with deterministic ordering.

WHERE filters individual rows before grouping. HAVING filters entire groups after aggregation.

Syntax

SELECT grouping_column, aggregate_function(col) AS alias
FROM table_name
GROUP BY grouping_column
HAVING aggregate_condition;

Example: departments with 5 or more employees

SELECT department_id, COUNT(*) AS headcount
FROM employees
GROUP BY department_id
HAVING COUNT(*) >= 5
ORDER BY headcount DESC;

HAVING vs WHERE

-- WHERE filters rows before grouping
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
WHERE hire_date > '2000-01-01'   -- only recent hires
GROUP BY department_id
HAVING AVG(salary) > 8000;       -- only departments with high avg salary

Execution order (full picture)

  1. FROM — identify source table
  2. WHERE — filter individual rows
  3. GROUP BY — form groups
  4. HAVING — filter groups
  5. SELECT — compute output columns
  6. ORDER BY — sort
  7. LIMIT / OFFSET — paginate

Can I use an alias in HAVING?

In most databases (including PostgreSQL), you cannot reference a SELECT alias in HAVING because SELECT runs after HAVING. Repeat the aggregate expression:

-- CORRECT
HAVING COUNT(*) >= 5

-- ERROR in most databases
HAVING headcount >= 5

Tips

  • HAVING without GROUP BY operates on the whole table as one group.
  • Any aggregate function (SUM, AVG, MIN, MAX) can appear in HAVING.

Learning context

This lesson is part of Aggregations & Grouping. Focus on the core idea in HAVING — filter on aggregates, then validate with deliberate practice.

What to master

  • Separate dimensions from measures
  • Group only by columns needed in final grain
  • Use HAVING only for aggregated filters

Common mistakes

  • Mixing row-level and aggregated logic
  • Grouping by too many columns
  • Using WHERE for aggregated conditions

High-level strategy

  • Draft base rows first
  • Add GROUP BY and verify expected number of groups
  • Add HAVING and ordering last

Task ladder

  1. Baseline query: get a correct first output.
  2. Add one complication: edge case, join, filter, or ranking rule.
  3. Re-run and refine until output is stable and explainable.

Transparent data checks

  • Check row count before and after each major clause.
  • Validate one manual sample row for correctness.
  • Confirm final ordering and column naming for trust.

Retention loop

  • Run the solution once.
  • Rewrite from memory with one variation.
  • Explain the logic in plain English in one sentence.

Logical reasoning for commands

GROUP BY

Why: Defines one output row per grouping key.

Memory cue: Group key = output row identity.

HAVING

Why: Filters after aggregates are calculated.

Memory cue: WHERE before groups, HAVING after groups.

COUNT / SUM / AVG

Why: Converts row-level data into business metrics.

Memory cue: Measure only after grain is correct.

Concept check

Quick check: HAVING — filter on aggregates

3 quick questions. One at a time. Instant score at the end.

Exercise 1

Departments with 5+ employees

Enterprise Workforce Analyticsmedium

Return department_id and employee_count for departments with 5 or more employees.

Run a query to see results here.
Exercise 2

Categories with average price above $50

Commerce Operations Intelligenceeasy

From the store sandbox, return category_id and avg_price (rounded to 2 decimal places) for product categories whose average price is above 50.

Run a query to see results here.
Exercise 3

Specialties with more than 2 doctors

Clinical Care Performanceeasy

From the hospital sandbox, return each specialty and doctor_count for specialties that have more than 2 doctors, ordered by specialty.

Run a query to see results here.

Lesson 3 of 4

0/3 solved (0%)

← Previous

Finish this lesson to unlock next.