SQLGROUP BY

GROUP BY

Aggregate functions like SUM and COUNT get far more useful once you can apply them separately to different subsets of rows — total sales per region, order count per customer, average score per class. GROUP BY is what makes that possible.
Grouping rows that share a value

GROUP BY collapses all rows that share the same value in a given column into a single output row, and lets you run aggregate functions across each of those groups independently:

Total sales per department

SQL
SELECT department, SUM(amount) AS total_sales
FROM sales
GROUP BY department;
 department  | total_sales
-------------+-------------
 Engineering |       48000
 Sales       |       92000
 Marketing   |       31000
Instead of one grand total across the whole table, you get one total per distinct department value.
The rule: SELECT columns must be grouped or aggregated
Every non-aggregated column must appear in GROUP BY
If a column appears in SELECT but isn’t wrapped in an aggregate function, it must also appear in the GROUP BY clause. Otherwise the database doesn’t know which single value to show for that column, since each group can contain many rows with different values:

Error — employee_name is not grouped or aggregated

SQL
-- This raises an error in PostgreSQL, MySQL (in strict mode), and SQL Server
SELECT department, employee_name, SUM(amount)
FROM sales
GROUP BY department;

Fixed — group by both columns, or aggregate employee_name

SQL
-- Option 1: group by both columns
SELECT department, employee_name, SUM(amount)
FROM sales
GROUP BY department, employee_name;

-- Option 2: aggregate employee_name instead (e.g. count how many contributed)
SELECT department, COUNT(DISTINCT employee_name) AS contributors, SUM(amount)
FROM sales
GROUP BY department;
Grouping by multiple columns
You can group by more than one column — each unique combination of values across those columns becomes its own group:

Sales per department, per quarter

SQL
SELECT department, quarter, SUM(amount) AS total_sales
FROM sales
GROUP BY department, quarter
ORDER BY department, quarter;
Logical execution order

It helps to know that GROUP BY runs after WHERE but before ORDER BY and before the final SELECT columns are computed. Conceptually:

  1. FROM — read the source table(s), applying any joins

  2. WHERE — filter individual rows, before any grouping happens

  3. GROUP BY — collapse the remaining rows into groups

  4. HAVING — filter entire groups (covered next)

  5. SELECT — compute the output columns and aggregates

  6. ORDER BY — sort the final result

Why WHERE can't filter on an aggregate
Because WHERE runs before GROUP BY, it has no concept of a group's aggregate value yet — an aggregate like SUM(amount) doesn't exist until grouping happens. That's exactly why HAVING exists as a separate, later filtering step.
  • GROUP BY collapses rows sharing the same value(s) into one row per group

  • Any SELECT column that isn't aggregated must appear in GROUP BY

  • You can group by multiple columns to get one row per unique combination

  • WHERE filters rows before grouping; HAVING filters entire groups after grouping