Learn SQL

SQL DISTINCT, GROUP BY, and HAVING: From Unique Values to Grouped Reports

DISTINCT cleans up repeated values, GROUP BY creates summaries per category, and HAVING filters those summaries after aggregation.

Published 2026-05-09·Updated 2026-05-09

From rows to categories

After you learn aggregate functions, the next step is asking for summaries by category: totals by region, counts by status, averages by department, and so on.

SQL for Files helps you practice these reporting patterns locally in the browser before applying them to your own CSV, JSON, or Parquet files.

DISTINCT removes duplicate result rows

DISTINCT is useful when you want a clean list of unique values, such as every region that appears in a sales table.

SELECT DISTINCT region
FROM sales
ORDER BY region;

DISTINCT applies to the full selected row

SELECT DISTINCT salesperson, region returns unique salesperson-region pairs, not just unique salespeople.

GROUP BY summarizes one group at a time

GROUP BY collects rows into groups before aggregate functions run. Instead of one total for the whole table, you get one total per group.

SELECT region, SUM(amount) AS total_amount
FROM sales
GROUP BY region
ORDER BY total_amount DESC;

HAVING filters after aggregation

WHERE filters individual rows before grouping. HAVING filters the grouped results after aggregate values exist.

SELECT salesperson, COUNT(*) AS sale_count
FROM sales
GROUP BY salesperson
HAVING COUNT(*) > 1;

Practice the DISTINCT and GROUP BY chapter

  1. Use DISTINCT to remove duplicate values.
  2. Use GROUP BY to summarize sales by region.
  3. Use HAVING to keep only groups that match aggregate conditions.

Continue in the editor

Open SQL for Files to add your own CSV, JSON, or Parquet files and try these examples locally in your browser.

Open editor