Understanding how to apply DISTINCT on multiple columns in SQL is essential for anyone who works with relational databases, because it allows you to eliminate duplicate rows based on the combination of values in two or more fields. Plus, this technique is frequently used when you need a clean list of unique combinations—such as distinct customer‑city pairs, unique product‑category groupings, or non‑repeating order‑date and employee‑id sets. In this article we will explore the concept, syntax, practical examples, performance implications, and best practices so you can confidently use DISTINCT across multiple columns in your queries Small thing, real impact..
Why Use DISTINCT on Multiple Columns?
When a table contains repetitive data, a simple SELECT DISTINCT column_name removes duplicates based on a single column. Even so, many real‑world scenarios require uniqueness across a set of columns. For example:
- A sales table may store the same order number multiple times because each line item appears on a separate row. To get a list of distinct orders you need to consider both
order_idandorder_date. - A user‑activity log might capture the same user visiting the same page several times in a minute; you want to know each unique user‑page combination regardless of timestamp.
- An inventory system could record multiple stock adjustments for the same product on the same warehouse; you need distinct product‑warehouse pairs to summarize current stock levels.
Using DISTINCT on multiple columns lets you collapse those repetitions into a single representative row, making downstream analysis, reporting, and data cleaning more efficient.
Basic Syntax
The SQL standard defines DISTINCT as a modifier that can follow the SELECT keyword. When you list more than one column after SELECT DISTINCT, the database evaluates the combination of values in those columns to determine uniqueness.
SELECT DISTINCT
col1,
col2,
col3
FROM table_name
WHERE condition;
col1, col2, col3– the columns whose combined values must be unique.FROM table_name– the source table.WHERE condition– optional filter to limit rows before applying distinctness.
Note that DISTINCT applies to the entire row defined by the selected columns; you cannot mix DISTINCT with non‑distinct expressions in the same SELECT list unless you use subqueries or aggregate functions Worth knowing..
Practical Examples
Example 1: Distinct Customer‑City Pairs
Suppose we have a customers table:
| customer_id | name | city | state |
|---|---|---|---|
| 1 | Anna | New York | NY |
| 2 | Bob | Los Angeles | CA |
| 3 | Anna | New York | NY |
| 4 | Carla | Chicago | IL |
| 5 | Bob | Los Angeles | CA |
To retrieve each unique combination of name and city:
SELECT DISTINCT
name,
city
FROM customers;
Result:
| name | city |
|---|---|
| Anna | New York |
| Bob | Los Angeles |
| Carla | Chicago |
Even though Anna and Bob appear twice, the duplicate rows are removed because the pair (name, city) is identical.
Example 2: Distinct Order‑Date and Employee Combinations
Consider an orders table:
| order_id | order_date | employee_id | amount |
|---|---|---|---|
| 101 | 2023-04-01 | 10 | 250 |
| 102 | 2023-04-01 | 10 | 180 |
| 103 | 2023-04-02 | 12 | 320 |
| 104 | 2023-04-02 | 10 | 400 |
| 105 | 2023-04-02 | 12 | 320 |
If we want to know which employees processed orders on each distinct date (ignoring the specific order ID and amount), we write:
SELECT DISTINCT
order_date,
employee_id
FROM orders
ORDER BY order_date, employee_id;
Result:
| order_date | employee_id |
|---|---|
| 2023-04-01 | 10 |
| 2023-04-02 | 10 |
| 2023-04-02 | 12 |
Notice that the duplicate row for employee 12 on 2023‑04‑02 appears only once.
Example 3: Using DISTINCT with Calculated Columns
You can also apply DISTINCT to expressions. Imagine a products table with price and discount_pct. To find unique effective prices after discount:
SELECT DISTINCT
price * (1 - discount_pct/100) AS effective_price
FROM products;
The database computes the expression for each row, then removes duplicates based on the resulting value Practical, not theoretical..
Alternative: GROUP BY for Distinctness
Although SELECT DISTINCT is straightforward, GROUP BY can achieve the same result and sometimes offers better performance, especially when you need aggregates alongside the distinct columns.
SELECT
col1,
col2
FROM table_name
GROUP BY col1, col2;
GROUP BY forces the engine to sort or hash the rows by the specified columns and return one row per group. If you do not need any aggregate functions (like COUNT, SUM), the output is identical to SELECT DISTINCT col1, col2 The details matter here..
When to prefer GROUP BY:
- You already need to compute aggregates (e.g.,
COUNT(*)per distinct combination). - The query planner shows that
GROUP BYuses a more efficient hash aggregation for large data sets. - You want to filter groups with
HAVING.
When to prefer DISTINCT:
- You only need distinct values and no extra aggregation.
- The query is simpler to read and maintain.
Performance Considerations
Using DISTINCT on multiple columns can be costly if the table is large or the columns are not indexed. Here are key factors that affect performance:
- Sorting vs. Hashing
Most databases implementDISTINCTby either sorting the result set and removing adjacent duplicates or by building a hash table. Sorting requiresO(n log n)time and may spill to disk if memory is limited. Hashing isO(n)on average but needs sufficient memory to hold the hash table.
2
-
Index Support
WhenDISTINCTis applied to a set of columns, the optimizer can take advantage of existing indexes to avoid a full table scan. If the columns are covered by a composite index (e.g.,CREATE INDEX idx_orders ON orders (order_date, employee_id)), the engine can read rows in index order and eliminate duplicates on the fly. This is far more efficient than scanning the heap and then sorting or hashing the result set. -
Data Types and Compression
Using appropriate data types reduces the memory footprint of the hash or sort operation. To give you an idea, storing dates asDATErather thanVARCHARallows tighter packing, and numeric columns benefit from precise types likeDECIMALinstead ofFLOATwhen exactness matters. Compressed columns (e.g.,VARCHARwith run‑length encoding) also lower I/O, which indirectly improvesDISTINCTperformance That alone is useful.. -
Result‑Set Size Management
If the distinct result set is expected to be large, consider materializing it in a temporary table or a CTE with anORDER BYclause that matches an index. This gives you the ability to page through the data (LIMIT/OFFSET) without re‑computing the distinct set each time, and it can also free up resources for concurrent workloads. -
Avoiding Unnecessary Distinctness
Sometimes a query can be rewritten to eliminateDISTINCTaltogether. To give you an idea, instead of joining two large tables and then applyingSELECT DISTINCT, you can useEXISTSorNOT INto test for the presence of a row without producing duplicate output. Similarly, aggregating withGROUP BYand selecting only the grouping columns yields the same effect while allowing you to add a lightweight aggregate (likeCOUNT(*)) if needed later. -
Parallel Execution and Memory Grants
Modern RDBMS engines can parallelize the sorting or hashing step forDISTINCT. On the flip side, each parallel thread still requires its own memory grant. Monitoring the memory usage of such queries helps you avoid “memory pressure” errors, especially on systems with limited RAM. Adjusting work‑memory settings or breaking the query into smaller chunks can keep the execution plan within safe limits. -
Using Window Functions for Deduplication
When you need to keep the first or last occurrence of a duplicate based on some ordering, a window function such asROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY …)can be more expressive thanDISTINCT. This approach lets you filter later (WHERE rn = 1) while preserving additional columns thatDISTINCTwould otherwise drop.
Practical Checklist
| ✅ | Action |
|---|---|
| Index | Ensure the columns used in DISTINCT are covered by a composite index. |
| Data Types | Use the smallest appropriate data types to reduce memory and I/O. Also, |
| Result Size | If the distinct set is huge, materialize it or paginate. |
| Rewrite | Look for opportunities to replace DISTINCT with EXISTS, NOT IN, or GROUP BY with aggregates. |
| Memory | Monitor memory grants and adjust work‑memory settings if needed. |
| Window Functions | Use ROW_NUMBER when you need to keep additional columns or apply custom ordering. |
By following these guidelines, you can make DISTINCT queries run faster, consume fewer resources, and integrate more cleanly into larger data‑processing pipelines.
Conclusion
SELECT DISTINCT remains a simple and readable way to eliminate duplicate rows, but its performance hinges on indexing, data types, and the size of the result set. Understanding when GROUP BY or window functions may be more efficient, and applying the checklist above, ensures you extract unique values without sacrificing query speed or system stability. With careful planning, distinct operations become a reliable building block in any SQL workflow.