Introduction
Until now, aggregate functions (like COUNT, SUM, and AVG) have produced a single summary result for the entire table.
One grand total for the whole dataset (e.g. ₹1,700 total sales).
One summary result calculated separately for each category (e.g. Electronics: ₹1,200, Clothing: ₹500).
Consider a realistic sales table:
| Product | Category | Amount |
|---|---|---|
| A | Electronics | ₹500 |
| B | Clothing | ₹300 |
| C | Electronics | ₹700 |
| D | Clothing | ₹200 |
What Does GROUP BY Do?
GROUP BY collects rows that share the same value(s) in specified columns into summary buckets:
FROM sales
GROUP BY category;
While grouping alone produces unique category rows, its true analytical power is unlocked when paired with aggregate functions.
Electronics
Electronics
Clothing
Clothing
Furniture
• [Electronics: 2 rows]
• [Clothing: 2 rows]
• [Furniture: 1 row]
GROUP BY + COUNT
The most intuitive example counts the number of rows belonging to each group:
FROM products
GROUP BY category;
Step-by-Step Breakdown:
- 1. Scan all rows in
products. - 2. Group rows by
category(Electronics group, Clothing group, Furniture group). - 3. Compute
COUNT(*)independently inside each group.
GROUP BY + SUM
Calculate total sales volume per category:
FROM sales
GROUP BY category;
Instead of adding all sales numbers together, SQL computes the sum for Electronics (500 + 700 = 1200) and Clothing (300 + 200 = 500) separately.
GROUP BY + AVG
Calculate departmental salary benchmarks:
FROM employees
GROUP BY department;
Each department receives its own isolated arithmetic mean.
GROUP BY + MIN / MAX
You can calculate multiple aggregate functions for each group simultaneously:
MIN(price) AS lowest_price,
MAX(price) AS highest_price
FROM products
GROUP BY category;
The Most Important Mental Model
Internalize this transformation pipeline as the cornerstone of SQL data analytics:
│ Electronics Bucket (2) │
│ Clothing Bucket (2) │
│ Furniture Bucket (1) │
└────────────────────────┘
GROUP BY vs DISTINCT
Returns only the unique category names by deduplicating rows. Does NOT perform aggregate calculations.
Creates group buckets specifically designed so that aggregate functions (COUNT, SUM, AVG) can compute per group.
GROUP BY Multiple Columns
Grouping by multiple columns forms groups based on each unique combination of column values:
FROM employees
GROUP BY city, department;
Here, Mumbai + Sales, Mumbai + HR, and Delhi + Sales are three separate, distinct groups.
Mumbai | Sales
Mumbai | HR
Delhi | Sales
• Mumbai + Sales (2 employees)
• Mumbai + HR (1 employee)
• Delhi + Sales (1 employee)
GROUP BY With WHERE
When a query includes both WHERE and GROUP BY:
FROM employees
WHERE city = 'Mumbai'
GROUP BY department;
Table ➔ WHERE (filters rows) ➔ GROUP BY (creates groups from remaining rows) ➔ Aggregate (calculates each group) ➔ Result.Understanding Query Structure
Look at this complete analytical query:
COUNT(*) AS employees,
AVG(salary) AS average_salary
FROM employees
WHERE city = 'Mumbai'
GROUP BY department;
- SELECT: Defines which group keys and aggregate results appear in the output.
- FROM: Identifies the source data table.
- WHERE: Filters individual records before grouping occurs.
- GROUP BY: Clusters matching rows into distinct groups.
- COUNT / AVG: Computes statistical calculations inside each bucket.
SELECT department, COUNT(*) AS total_employees FROM employees GROUP BY department;
| department | COUNT Result |
|---|---|
| Sales | 4 employees |
| HR | 2 employees |
| Engineering | 2 employees |
Common GROUP BY Mistakes
SELECT department, employee, COUNT(*) ... GROUP BY department; is invalid in standard SQL because the database does not know which employee row to pick for a collapsed department group.
WHERE filters raw rows before grouping. You cannot write WHERE COUNT(*) > 2 because groups do not exist yet when WHERE executes (filtering groups requires HAVING).
DISTINCT only eliminates duplicate rows in the final output; it cannot compute per-group summaries like SUM, AVG, or COUNT.
Practical Step-by-Step Exercises
| Task Goal | Target Table | Required SQL Solution | Grouping Pattern |
|---|---|---|---|
| 1. Count Employees per Dept | employees | SELECT department, COUNT(*) FROM employees GROUP BY department; | Single-column COUNT |
| 2. Total Sales per Category | sales | SELECT category, SUM(amount) FROM sales GROUP BY category; | Single-column SUM |
| 3. Average Salary per Dept | employees | SELECT department, AVG(salary) FROM employees GROUP BY department; | Single-column AVG |
| 4. Min/Max Price per Category | products | SELECT category, MIN(price), MAX(price) FROM products GROUP BY category; | Multi-aggregate group |
| 5. City + Department Grouping | employees | SELECT city, department, COUNT(*) FROM employees GROUP BY city, department; | Multi-column grouping |
| 6. Filtered Department Count | employees | SELECT department, COUNT(*) FROM employees WHERE city = 'Mumbai' GROUP BY department; | WHERE + GROUP BY |
GROUP BY Best Practices
- Match SELECT Columns with GROUP BY: Ensure every non-aggregated column in your
SELECTlist is explicitly included inGROUP BY. - Meaningful Aliases: Always assign descriptive aliases (e.g.
COUNT(*) AS total_employees) for readable report headers. - Filter Early with WHERE: Reduce row count using
WHEREbefore grouping to improve query performance. - Order Your Results: Combine with
ORDER BY(e.g.ORDER BY total_sales DESC) to rank your grouped summaries.
What You Should Know Now
- ✓GROUP BY: Transforms rows into grouped summary buckets
- ✓Per-Group Aggregates: COUNT, SUM, AVG, MIN, MAX calculate per bucket
- ✓Multi-Column: Groups by unique combinations of columns
- ✓WHERE + GROUP BY: Rows are filtered first before grouping
- ✓GROUP BY vs DISTINCT: GROUP BY enables statistical aggregations
- ✓One Row Output: Each distinct group produces exactly 1 result row
🎯 Knowledge Check Quiz: SQL GROUP BY
Test your understanding of group clustering, per-group aggregates, multi-column combinations, and the WHERE + GROUP BY mental model.