SQLab Hub
Lesson 2 · DISTINCT
Lesson 2 · Fundamentals

SQL SELECT DISTINCT: Remove Duplicate Rows

SELECT DISTINCT returns each different row once. The 200 Playground orders were shipped to 20 cities, so SELECT DISTINCT shipping_city FROM orders gives 20 rows instead of 200. With several columns, it is the combination that has to be unique.

Unique values Multiple columns · COUNT(DISTINCT) ▶ Runnable Playground examples 🧪 5 practice exercises 📝 5-question quiz ⏱ ~12 min

What Does DISTINCT Do?

Without DISTINCT, every row comes back, duplicates included. Compare the number of order rows with the number of different statuses:

SQLite · runs in the Playground
SELECT COUNT(status) AS status_values,
       COUNT(DISTINCT status) AS different_statuses
FROM orders;

▶ Run this query in the SQL Playground

Status values vs different statuses
status_valuesdifferent_statuses
2006
✓ 1 row · real output from the Playground database

DISTINCT lists those different values:

SQLite · runs in the Playground
SELECT DISTINCT status
FROM orders
ORDER BY status;

▶ Run this query in the SQL Playground

The different order statuses
status
cancelled
delivered
pending
processing
refunded
shipped
✓ 6 rows · real output from the Playground database
SQL · syntax
SELECT DISTINCT column_1, column_2, ...
FROM table
[WHERE ...]
[ORDER BY ...];

DISTINCT goes right after SELECT and applies to the whole row of selected columns. It is removed from the result after the rows are found; it does not change how they are read from the table.

DISTINCT With Multiple Columns

With more than one column, a row is a duplicate only if every selected column matches. Each department and city where employees work:

SQLite · runs in the Playground
SELECT DISTINCT department, city
FROM employees
WHERE department = 'Engineering'
ORDER BY city;

▶ Run this query in the SQL Playground

Cities where Engineering employees work
departmentcity
EngineeringFort Worth
EngineeringHouston
EngineeringJacksonville
EngineeringPhiladelphia
EngineeringSan Diego
EngineeringSan Jose
✓ 6 rows · real output from the Playground database

Engineering appears on several rows because it has employees in several cities. Each department and city pair appears once.

DISTINCT is not a function. SELECT DISTINCT(department), city looks as if it applies to department only, but the brackets are just around a column; DISTINCT still applies to the pair. PostgreSQL has DISTINCT ON (column) for "one row per value of this column", which other databases do not support.

Counting Unique Values

COUNT(DISTINCT column) counts different values instead of rows. How many customers have ordered, compared with how many orders there are:

SQLite · runs in the Playground
SELECT COUNT(*) AS orders,
       COUNT(DISTINCT user_id) AS customers_who_ordered,
       COUNT(DISTINCT shipping_city) AS cities
FROM orders;

▶ Run this query in the SQL Playground

Orders, distinct customers and distinct cities
orderscustomers_who_orderedcities
20011420
✓ 1 row · real output from the Playground database

It works per group too. Distinct customers per order status:

SQLite · runs in the Playground
SELECT status,
       COUNT(*) AS orders,
       COUNT(DISTINCT user_id) AS customers
FROM orders
GROUP BY status
ORDER BY orders DESC;

▶ Run this query in the SQL Playground

Orders and distinct customers per status
statusorderscustomers
delivered8265
processing2824
cancelled2725
pending2322
shipped2120
refunded1918
✓ 6 rows · real output from the Playground database

SQLite and SQL Server accept only one column inside COUNT(DISTINCT ...). To count distinct pairs, count the rows of a DISTINCT subquery: SELECT COUNT(*) FROM (SELECT DISTINCT department, city FROM employees). MySQL and PostgreSQL also accept COUNT(DISTINCT department, city) or a row value.

DISTINCT and NULL

For DISTINCT, all NULLs count as the same value, so NULL appears at most once in the result. COUNT(DISTINCT ...) leaves NULL out entirely:

SQLite · runs in the Playground
SELECT COUNT(DISTINCT manager_id) AS distinct_managers,
       (SELECT COUNT(*) FROM (SELECT DISTINCT manager_id FROM employees)) AS distinct_rows_including_null
FROM employees;

▶ Run this query in the SQL Playground

NULL in DISTINCT and in COUNT(DISTINCT)
distinct_managersdistinct_rows_including_null
1011
✓ 1 row · real output from the Playground database

There are 10 different managers. SELECT DISTINCT returns one more row, the single NULL for employees who have no manager.

DISTINCT vs GROUP BY

For a plain list of unique values, these two queries return the same rows:

SQLite · runs in the Playground
SELECT category
FROM products
GROUP BY category
ORDER BY category;

▶ Run this query in the SQL Playground

Unique categories with GROUP BY
category
Automotive
Books
Clothing
Electronics
Food & Beverage
Health & Beauty
Home & Garden
Office Supplies
Sports & Outdoors
Toys
✓ 10 rows · real output from the Playground database
SQL · the same result with DISTINCT
SELECT DISTINCT category
FROM products
ORDER BY category;
DISTINCTGROUP BY
PurposeRemove duplicate rowsBuild groups to aggregate
Aggregates per valueNoYes: COUNT, SUM, AVG ... per group
Filter on the groupsNoHAVING
Performance for a plain listUsually identicalUsually identical

Use DISTINCT when you only want the list; use GROUP BY as soon as you want a number per value.

DISTINCT With ORDER BY and LIMIT

DISTINCT is applied before ORDER BY and LIMIT, so LIMIT 5 gives five different values:

SQLite · runs in the Playground
SELECT DISTINCT shipping_city
FROM orders
ORDER BY shipping_city
LIMIT 5;

▶ Run this query in the SQL Playground

First five shipping cities alphabetically
shipping_city
Austin
Charlotte
Chicago
Columbus
Dallas
✓ 5 rows · real output from the Playground database

Most databases only let you ORDER BY columns that are in the SELECT DISTINCT list. Sorting by a column that is not selected would be ambiguous: which of the duplicate rows should decide the position?

Common DISTINCT Mistakes

Five errors and how to avoid them.

1. Expecting DISTINCT to apply to one column only

❌ Pairs are unique, not departments
SQL
SELECT DISTINCT department, first_name
FROM employees;
✅ Select only what must be unique
SQL
SELECT DISTINCT department
FROM employees;

DISTINCT works on the whole selected row. Every extra column makes more rows distinct.

2. DISTINCT outside COUNT

❌ Returns 200
SQL
SELECT DISTINCT COUNT(user_id)
FROM orders;
✅ Returns 114
SQL
SELECT COUNT(DISTINCT user_id)
FROM orders;

Outside, DISTINCT de-duplicates the single result row; inside, it de-duplicates the values before counting.

3. Using DISTINCT to hide a join problem

❌ Covers up duplicate rows
SQL
SELECT DISTINCT u.id, u.first_name
FROM users u
JOIN orders o ON o.user_id = u.id;
✅ Fix the join
SQL
SELECT u.id, u.first_name
FROM users u
WHERE EXISTS (SELECT 1 FROM orders o
              WHERE o.user_id = u.id);

The join creates one row per order. If you only need users who ordered, EXISTS says so without producing duplicates to remove.

4. Writing UNIQUE instead of DISTINCT

❌ Not standard
SQL
SELECT UNIQUE category FROM products;
✅ DISTINCT
SQL
SELECT DISTINCT category FROM products;

UNIQUE only works in Oracle as a synonym. DISTINCT works everywhere.

5. DISTINCT on large results by habit

❌ An extra sort or hash for nothing
SQL
SELECT DISTINCT id, email
FROM users;
✅ Only when duplicates are possible
SQL
SELECT id, email
FROM users;

id is already unique, so DISTINCT only adds work. On large tables that work is noticeable.

Practice DISTINCT Queries

5 exercises on the real Playground tables.

Write each query yourself first. Every solution has been run against the Playground database.

  1. 1Easy List every department name once, in alphabetical order. Table: employees

    Show solution
    SQLite · runs in the Playground
    SELECT DISTINCT department
    FROM employees
    ORDER BY department;

    ▶ Run this query in the SQL Playground

    DISTINCT removes the repeats; ORDER BY sorts them.

  2. 2Easy How many different countries do users come from? Table: users

    Show solution
    SQLite · runs in the Playground
    SELECT COUNT(DISTINCT country) AS countries
    FROM users;

    ▶ Run this query in the SQL Playground

    COUNT(DISTINCT ...) counts each country once.

  3. 3Medium List each payment method together with each status it has had. Table: payments

    Show solution
    SQLite · runs in the Playground
    SELECT DISTINCT method, status
    FROM payments
    ORDER BY method, status;

    ▶ Run this query in the SQL Playground

    With two columns, each method and status pair appears once.

  4. 4Medium How many different products have been ordered, and in how many different orders? Table: order_items

    Show solution
    SQLite · runs in the Playground
    SELECT COUNT(DISTINCT product_id) AS products_ordered,
           COUNT(DISTINCT order_id) AS orders
    FROM order_items;

    ▶ Run this query in the SQL Playground

    Two COUNT(DISTINCT ...) in one query, each on its own column.

  5. 5Medium For each department, count the different cities its employees work in. Table: employees

    Show solution
    SQLite · runs in the Playground
    SELECT department, COUNT(DISTINCT city) AS cities
    FROM employees
    GROUP BY department
    ORDER BY cities DESC, department;

    ▶ Run this query in the SQL Playground

    COUNT(DISTINCT city) runs once per department group.

📝 Quiz — DISTINCT

Test your understanding · 0/5 answered

Question 1 of 5

What does SELECT DISTINCT do?

Question 2 of 5

Which query lists each department once?

Question 3 of 5

What is treated as unique in SELECT DISTINCT department, city?

Question 4 of 5

How do you find which payment methods are used in a large payments table?

Question 5 of 5

Which statement about DISTINCT is false?

DISTINCT Interview Questions

Q1. What is the difference between DISTINCT and GROUP BY?

For a plain list they return the same rows and usually perform the same. GROUP BY is needed when you want aggregates per value or a HAVING filter.

Q2. How does DISTINCT treat NULL?

All NULLs count as one value, so NULL appears at most once. COUNT(DISTINCT col) ignores NULLs completely.

Q3. How do you count distinct combinations of two columns?

SELECT COUNT(*) FROM (SELECT DISTINCT a, b FROM t) x works everywhere. MySQL also allows COUNT(DISTINCT a, b).

Q4. Why is SELECT DISTINCT after a JOIN often a warning sign?

It usually removes duplicates the join created. If the duplicates are not wanted, EXISTS or a corrected join condition is clearer and avoids producing them.

Q5. What is DISTINCT ON in PostgreSQL?

It keeps the first row for each value of the listed columns, using ORDER BY to decide which row is first. Other databases use ROW_NUMBER() for the same result.

Frequently Asked Questions

What does SELECT DISTINCT do in SQL?

SELECT DISTINCT removes duplicate rows from the result, so each different row appears once. SELECT DISTINCT status FROM orders returns the six statuses instead of 200 rows.

Can SELECT DISTINCT work with multiple columns?

Yes. DISTINCT applies to the combination of all selected columns: SELECT DISTINCT department, city returns each department and city pair once.

Is SELECT DISTINCT the same as GROUP BY?

For a plain list of unique values, they return the same rows. GROUP BY is the one to use when you also want a count, sum or average per value.

How does DISTINCT handle NULL values in SQL?

DISTINCT treats all NULLs as one value, so NULL appears at most once. COUNT(DISTINCT column) does not count NULL at all.

How do you count unique values in SQL?

Use COUNT(DISTINCT column). SELECT COUNT(DISTINCT user_id) FROM orders counts the different customers who placed orders: 114 in the Playground.

Can I use SELECT DISTINCT with ORDER BY?

Yes. DISTINCT is applied first and ORDER BY then sorts the unique rows. Most databases only allow ORDER BY on the columns in the SELECT DISTINCT list.

Where can I practise SELECT DISTINCT queries?

In the SQLabHub Playground: every query on this page has a "Run this query" link that opens it there against the same practice database.