SQL DISTINCT vs GROUP BY: When They're the Same (and When They're Not)
Without an aggregate function, DISTINCT and GROUP BY can produce identical results — which is exactly why people confuse them. The moment an aggregate enters the picture, only one of them can do the job. Here's the actual boundary between the two.
Two tools, one overlapping use case
DISTINCT removes duplicate rows from a result set. GROUP BY collapses rows into groups so aggregate functions (COUNT, SUM, AVG...) can summarise each group. When there's no aggregate involved, they can produce the exact same output — which is where the confusion starts.
When they produce identical results
Both of these return every distinct department_id that appears in the table — same rows, same order-independent result.
SELECT DISTINCT department_id FROM employees;
SELECT department_id FROM employees GROUP BY department_id;
Why they overlap here
With no aggregate function in the SELECT list, GROUP BY department_id just forms one group per distinct department_id value and returns one row per group — which is exactly what DISTINCT does directly. On this specific query shape, most optimizers produce the same or a very similar execution plan for both versions.
Where they stop being equivalent
The overlap only holds when there's no aggregate function. The moment you need a per-group summary value — a count, a total, an average — DISTINCT can't do the job at all. It operates on the final row set; it has no concept of "per-group" calculation.
GROUP BY does something DISTINCT structurally can't
Count employees per department — this requires GROUP BY; there's no DISTINCT equivalent.
SELECT department_id, COUNT(*) AS emp_count
FROM employees
GROUP BY department_id
ORDER BY emp_count DESC;
-- SELECT DISTINCT department_id, COUNT(*) FROM employees; -- not the same thing —
-- without GROUP BY, COUNT(*) here would count ALL rows, and DISTINCT would
-- then just remove duplicate (department_id, total_count) pairs, which
-- isn't a per-department count at all
How each handles multiple columns
SELECT DISTINCT department_id, salary FROM employees does not give you "distinct departments with a representative salary" — it gives you every distinct pairing of the two columns, which is rarely what people expect on first read.
| DISTINCT | GROUP BY | |
|---|---|---|
| Dedups based on | every selected column, together | the columns listed in GROUP BY |
| Can mix a deduped column with a per-row varying value? | no — that would defeat deduplication | yes, if that value is wrapped in an aggregate |
| SELECT DISTINCT department_id, salary | keeps a row for every unique (department_id, salary) pair | n/a — salary would need an aggregate or its own GROUP BY entry |
Is one faster?
For the no-aggregate, single-column case where they're equivalent, performance is typically similar — both usually require a sort or hash operation to identify duplicates, and modern optimizers often choose comparable plans for either. The real efficiency question is whether you're using the right tool for the job at all: reaching for DISTINCT when you actually need a per-group aggregate produces wrong results, not just a slower query, and reaching for GROUP BY when you just need unique values adds unnecessary conceptual overhead without changing the outcome. Always check an execution plan if performance genuinely matters for a specific query.