SQLQuokka
Start practicing
QuestionsLearnSandboxFeaturesRoadmapFAQ
SQL Concept

SQL GROUP BY Explained with Examples

GROUP BY collapses rows that share a value into one row per group, so you can count, sum or average each group. It is behind most analytics questions: revenue per month, users per country, orders per customer.

Examples run in MySQL 8 and PostgreSQL · Updated

In short

  • Every selected column must be grouped or wrapped in an aggregate.
  • COUNT(*) counts rows; COUNT(col) skips NULLs.
  • NULL values form their own group.
  • Filter rows with WHERE before grouping, groups with HAVING after.

What happens to the rows

Picture the table sorted into piles, one pile per dept_id. Each pile becomes a single output row, and aggregate functions (COUNT, SUM, AVG, MIN, MAX) summarise the pile.

Because a pile becomes one row, you cannot select a plain column like name unless it is in GROUP BY: the database would not know which name from the pile to show. PostgreSQL rejects it; MySQL rejects it too under its default ONLY_FULL_GROUP_BY mode.

NULL is a group too

Rows where the grouping column is NULL are not dropped; they form one NULL group. In the example below, Zoya (no department) appears as the NULL group. Where that group sorts differs between databases, so sort by something explicit.

The example tables

Every example on this page uses these tables. They are small on purpose, so you can check each result by eye.

employees

emp_idnamedept_idsalarymanager_idhire_date
1Aarav1095000NULL2021-03-01
2Diya108800012022-06-15
3Kabir107200012023-01-10
4Meera2064000NULL2020-11-20
5Rohan205800042024-02-05
6Isha3061000NULL2022-09-12
7Arjun206400042023-07-30
8ZoyaNULL52000NULL2024-05-01

Examples

Count and average per department

One row per dept_id, including the NULL group for Zoya. ROUND keeps the average readable.

SELECT dept_id, COUNT(*) AS employees, ROUND(AVG(salary)) AS avg_salary
FROM employees
GROUP BY dept_id
ORDER BY employees DESC, avg_salary DESC;
Result
dept_idemployeesavg_salary
10385000
20362000
30161000
NULL152000

Several aggregates at once

WHERE removes Zoya before grouping; each department shows its top salary and earliest hire date.

SELECT dept_id, MAX(salary) AS top_salary, MIN(hire_date) AS first_hire
FROM employees
WHERE dept_id IS NOT NULL
GROUP BY dept_id
ORDER BY dept_id;
Result
dept_idtop_salaryfirst_hire
10950002021-03-01
20640002020-11-20
30610002022-09-12

Common mistakes

  • Selecting an ungrouped column. Add the column to GROUP BY, wrap it in an aggregate, or use a window function if you need row-level detail next to group totals.
  • COUNT(col) vs COUNT(*). COUNT(manager_id) skips employees without a manager. Use COUNT(*) to count rows, COUNT(DISTINCT col) to count unique values.
  • Integer averages. Averages and ratios of integers can lose decimals in some databases. Multiply by 1.0 or use ROUND(…, 2) when the question asks for decimals.

Practise GROUP BY

Interview questions that use this concept, easiest first. Each one has the step-by-step visualizer and answer checking.

  1. Multilingual Language Speakers

    Identify users who speak at least two distinct target languages ('English', 'Spanish', 'German') from skill profile records.

    Easy✓ Solved
  2. Daily Active Users (DAU)

    Compute the daily count of unique active users from raw product activity event timestamps.

    Easy✓ Solved
  3. Ad Click-Through Rate (CTR)

    Calculate the percentage CTR per ad campaign rounded to 2 decimals, safely handling campaigns with zero impressions.

    Easy✓ Solved
  4. Play Store: Top Rated App Categories

    Find categories with at least 3 apps rated 4.0 or higher, returning the category, app count, and average rating rounded to 2 decimals.

    Easy✓ Solved
  5. Google Maps: High-Impact Local Guides

    Find verified reviewers who submitted at least 2 photo reviews in San Francisco or Mountain View.

    Easy✓ Solved
  6. Google Drive: External Sensitive File Shares

    Audit sensitive files shared with non-Google email addresses to identify potential data exposure risks.

    Easy✓ Solved

Questions people ask

Can I GROUP BY more than one column?
Yes. GROUP BY dept_id, EXTRACT(YEAR FROM hire_date) makes one group per department and year combination.
Does GROUP BY sort the result?
Not reliably. Add ORDER BY whenever the order matters.