Lesson 2 of 4

Progress: 0/4 exercises solved (0%)

← Previous

Solve all exercises below to unlock the next lesson.

GROUP BY

Lesson Description

One row per group instead of one per input row.

Easy Project

Mini project: Produce an executive KPI table with totals and averages per department, sorted by highest business impact.

GROUP BY — aggregate per group

GROUP BY splits rows into groups by the values of one or more columns, then applies an aggregate function to each group.

Syntax

SELECT grouping_column, aggregate_function(col)
FROM table_name
GROUP BY grouping_column;

Example: headcount per department

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

One row is returned per distinct department_id.

Multiple grouping columns

SELECT department_id, job_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id, job_id
ORDER BY department_id, job_id;

The golden rule

Every column in SELECT must either be listed in GROUP BY or wrapped in an aggregate function. Non-grouped, non-aggregated columns are ambiguous — PostgreSQL will reject them.

-- ERROR: first_name not in GROUP BY and not aggregated
SELECT department_id, first_name, COUNT(*)
FROM employees
GROUP BY department_id;

Execution order

WHEREGROUP BYSELECTORDER BY

You can filter rows before grouping with WHERE, and filter groups after grouping with HAVING (next lesson).

Tips

  • GROUP BY can use a column position number: GROUP BY 1 means the first column in SELECT.
  • You can GROUP BY an expression: GROUP BY DATE_TRUNC('month', hire_date).

Learning context

This lesson is part of Aggregations & Grouping. Focus on the core idea in GROUP BY, 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: GROUP BY

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

Exercise 1

Employee count per department

Enterprise Workforce Analyticsmedium

Return department_id and employee_count for every department in the HR sandbox.

Run a query to see results here.
Exercise 2

Orders by status

Commerce Operations Intelligencemedium

From the store sandbox, return status and the number of orders (order_count) in each status.

Run a query to see results here.
Exercise 3

Product count per category

Commerce Operations Intelligenceeasy

From the store sandbox, return category_id and product_count (the number of products in each category), ordered by category_id.

Run a query to see results here.
Exercise 4

Appointments per status

Clinical Care Performanceeasy

From the hospital sandbox, return each appointment status and the number of appointments in that status (appt_count), ordered by status.

Run a query to see results here.

Lesson 2 of 4

0/4 solved (0%)

← Previous

Finish this lesson to unlock next.