DISTINCT ON

DISTINCT ON explained with practical SQL patterns, edge cases, and production-ready guidance.

5 min read · Last updated: March 2026 · Back to overview

Quick Answer

DISTINCT ON (column) is a PostgreSQL-specific clause that keeps only the first row for each unique value of the specified column(s). An ORDER BY clause controls which row is considered "first" — the DISTINCT ON expression must match the leftmost ORDER BY expression.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

Introduction to PostgreSQL DISTINCT ON

DISTINCT ON is a PostgreSQL extension to standard SQL that lets you retrieve the first row per unique value of one or more specified columns, while keeping all other columns from that row in the result. This is particularly useful when you want the "latest," "highest," or "best" record per group without a subquery or window function.

The basic syntax is:

SELECT DISTINCT ON (column1, column2, ...)
  column1, column2, other_columns, ...
FROM table_name
ORDER BY column1, column2, ...;

The key rule: the expressions inside DISTINCT ON (...) must match the leftmost expressions in the ORDER BY clause. The remaining ORDER BY expressions determine which row within each group is kept as "first."

DISTINCT ON example: highest score per student

Consider a student_scores table with scores for multiple subjects per student:

CREATE TABLE student_scores (
  id      SERIAL PRIMARY KEY,
  name    VARCHAR(50) NOT NULL,
  subject VARCHAR(50) NOT NULL,
  score   INTEGER NOT NULL
);

INSERT INTO student_scores (name, subject, score) VALUES
  ('Alice',   'Math',    90),
  ('Bob',     'Math',    85),
  ('Alice',   'Physics', 92),
  ('Bob',     'Physics', 88),
  ('Charlie', 'Math',    95),
  ('Charlie', 'Physics', 90);

To retrieve each student's highest score across all subjects:

SELECT DISTINCT ON (name)
  name, subject, score
FROM student_scores
ORDER BY name, score DESC;

Result:

name    | subject | score
---------+---------+-------
Alice   | Physics |    92
Bob     | Physics |    88
Charlie | Math    |    95
(3 rows)

Here DISTINCT ON (name) keeps one row per student, and ORDER BY name, score DESC ensures that the row with the highest score is the one retained.

Most recent order per customer

A common application pattern: finding the latest order for each customer:

SELECT DISTINCT ON (customer_id)
  customer_id, order_id, placed_at, total_amount
FROM orders
ORDER BY customer_id, placed_at DESC;

This is more concise than the equivalent subquery approach and typically performs well with a compound index on (customer_id, placed_at DESC).

DISTINCT ON vs. SELECT DISTINCT

SELECT DISTINCT eliminates rows that are completely identical across all selected columns. DISTINCT ON (col) keeps one row per unique value of col — the other columns in that row can vary. They solve fundamentally different problems:

  • Use SELECT DISTINCT to deduplicate rows where all columns match.
  • Use DISTINCT ON to get one representative row per group (like the row with the max or most recent value).

Important rules and tips

  • The expression in DISTINCT ON (...) must be the leftmost expression in the ORDER BY clause. Misaligning them causes an error.
  • Omitting ORDER BY entirely is allowed but makes the "first" row selection unpredictable — always include it in practice.
  • For performance on large tables, create a composite index matching your ORDER BY columns (e.g., CREATE INDEX ON orders (customer_id, placed_at DESC)).
  • DISTINCT ON is PostgreSQL-specific. If cross-database portability matters, use a subquery with ROW_NUMBER() instead.

Reference: PostgreSQL documentation — SELECT DISTINCT ON.

Continue in Querying Data: Back to tutorial overview.

Related in this section: PostgreSQL SELECT · Column Aliases · ORDER BY

Frequently Asked Questions

What is the difference between DISTINCT ON and SELECT DISTINCT?

SELECT DISTINCT removes rows where all selected columns are identical. DISTINCT ON (col) keeps one row per unique value of col — the remaining columns in that row can differ across rows. DISTINCT ON is designed for "get first/latest/max per group" queries, while SELECT DISTINCT is for simple deduplication.

Why does PostgreSQL require ORDER BY with DISTINCT ON?

Without ORDER BY, PostgreSQL has no defined way to determine which row within a group is "first." The ORDER BY clause makes the selection deterministic. The DISTINCT ON expression must match the leftmost ORDER BY expression — this rule is enforced by the query planner.

Can I use DISTINCT ON with multiple columns?

Yes. DISTINCT ON (col1, col2) treats the combination of col1 and col2 as the grouping key, keeping one row per unique (col1, col2) pair. The ORDER BY clause must start with col1, col2 in the same order.

Is DISTINCT ON available in other databases like MySQL or SQL Server?

No. DISTINCT ON is a PostgreSQL-specific extension. In other databases, the equivalent pattern uses a subquery with ROW_NUMBER() OVER (PARTITION BY col ORDER BY sort_col DESC) and then filters WHERE rn = 1.

How do I make a DISTINCT ON query faster?

Create a composite index that matches the ORDER BY columns. For example, if your query is SELECT DISTINCT ON (customer_id) ... ORDER BY customer_id, placed_at DESC, create an index on (customer_id, placed_at DESC). PostgreSQL can then use an index scan to retrieve rows in the right order without a separate sort step, which is especially effective for large tables.