Home Functions FIRST_VALUE()
SQL Window Function

FIRST_VALUE()

FIRST_VALUE() is a window function that returns the value from the first row of the window frame, letting you copy the top row of each group onto every row.

MySQLPostgreSQLSQL ServerSQLite
Returns: The value of the chosen expression from the first row of the window frame. Its type matches the input expression, and it can be NULL if that first row is NULL.

Syntax

FIRST_VALUE(expression) OVER (ORDER BY ...)FIRST_VALUE(expression) OVER (PARTITION BY ... ORDER BY ...)FIRST_VALUE(expression) OVER (PARTITION BY ... ORDER BY ... ROWS ...)
ParameterTypeRequiredDescription
expression column or expression yes The value to read from the first row of the frame. Usually a column, but any scalar expression is allowed.
PARTITION BY clause no Splits the rows into independent groups. FIRST_VALUE restarts at the top of each partition.
ORDER BY clause yes Defines what first means. Without it the order is undefined and the result is not meaningful.

How it works

FIRST_VALUE() is a window function. Instead of collapsing rows the way an aggregate does, it looks along an ordered window and returns the value found in the first row of that window, repeating it on every row of the group.

The typical shape is FIRST_VALUE(column) OVER (PARTITION BY group_col ORDER BY sort_col). The ORDER BY decides which row counts as first, and the optional PARTITION BY restarts the calculation for each group. This is the standard way to bring the top row of a group onto every row: the best selling product per category, the first order date per customer, or the highest score in each class.

Once the first value sits on every row you can compare each row against it, for example measuring how far each order is from a customer first order, or expressing a value as a share of the group leader. FIRST_VALUE is available in MySQL 8.0 and later, SQLite 3.25 and later, and in all supported versions of PostgreSQL and SQL Server. Its mirror image is LAST_VALUE(), and NTH_VALUE() reaches any position in between.

Examples

First order date per customer

SELECT customer_id, order_id, order_date,
       FIRST_VALUE(order_date)
         OVER (PARTITION BY customer_id ORDER BY order_date) AS first_order
FROM orders
ORDER BY customer_id, order_date;
Result
customer_id | order_id | order_date | first_order
------------+----------+------------+------------
          1 |      101 | 2026-01-03 | 2026-01-03
          1 |      140 | 2026-02-11 | 2026-01-03
          2 |      118 | 2026-01-20 | 2026-01-20

Best score in each group on every row

-- highest score per class, shown against each student
SELECT class, student, score,
       FIRST_VALUE(score)
         OVER (PARTITION BY class ORDER BY score DESC) AS top_score
FROM exam_results
ORDER BY class, score DESC;
Result
class | student | score | top_score
------+---------+-------+----------
 A     | Sara    |    95 |       95
 A     | Omar    |    88 |       95
 B     | Lina    |    91 |       91

Compare each row to the group first value

-- days since the customer first order
SELECT customer_id, order_date,
       order_date - FIRST_VALUE(order_date)
         OVER (PARTITION BY customer_id ORDER BY order_date) AS days_since_first
FROM orders
ORDER BY customer_id, order_date;
Result
customer_id | order_date | days_since_first
------------+------------+-----------------
          1 | 2026-01-03 |               0
          1 | 2026-02-11 |              39
          2 | 2026-01-20 |               0

Share of the best selling product

-- each product sales as a share of the category leader
SELECT category, product, units,
       ROUND(100.0 * units / FIRST_VALUE(units)
         OVER (PARTITION BY category ORDER BY units DESC), 1) AS pct_of_top
FROM product_sales
ORDER BY category, units DESC;
Result
category | product | units | pct_of_top
---------+---------+-------+-----------
 Phones   | P-Max   |   900 |      100.0
 Phones   | P-Mini  |   540 |       60.0
 Tablets  | T-10    |   300 |      100.0

First status change per ticket

SELECT ticket_id, changed_at, status,
       FIRST_VALUE(status)
         OVER (PARTITION BY ticket_id ORDER BY changed_at) AS opening_status
FROM ticket_history
ORDER BY ticket_id, changed_at;
Result
ticket_id | changed_at | status  | opening_status
----------+------------+---------+---------------
       10 | 2026-03-01 | open    | open
       10 | 2026-03-02 | pending | open
       11 | 2026-03-01 | open    | open

Common mistakes

Wrong
-- No ORDER BY: which row is first is undefined,
-- so first_order can differ between runs
SELECT customer_id, order_date,
       FIRST_VALUE(order_date)
         OVER (PARTITION BY customer_id) AS first_order
FROM orders;
Right
SELECT customer_id, order_date,
       FIRST_VALUE(order_date)
         OVER (PARTITION BY customer_id ORDER BY order_date) AS first_order
FROM orders;

FIRST_VALUE needs an ORDER BY inside OVER to define what first means. Without it the window has no order and the engine may return any row, so the result is not stable.

Wrong
-- Expecting the last value here, but this returns
-- the FIRST value: the default frame runs from the
-- start of the partition to the current row
SELECT category, product, units,
       LAST_VALUE(units)
         OVER (PARTITION BY category ORDER BY units DESC) AS top_units
FROM product_sales;
Right
-- FIRST_VALUE needs no special frame, because the
-- default frame already starts at the first row
SELECT category, product, units,
       FIRST_VALUE(units)
         OVER (PARTITION BY category ORDER BY units DESC) AS top_units
FROM product_sales;

The default frame ends at the current row, so FIRST_VALUE is usually correct as written. LAST_VALUE() is the one that surprises people: it needs an explicit full frame such as ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING, otherwise it returns the current row rather than the last.

Performance

FIRST_VALUE() is cheap once the rows are ordered: it reads a single value from the first row of the frame. The real cost is the sort that the ORDER BY and PARTITION BY imply. An index that already matches the partition and order columns lets the engine skip that sort.

Because the default frame stretches from the first row of the partition to the current row, FIRST_VALUE does not force the whole frame to be materialised the way an unbounded-following frame does. If you need the last row instead, be aware that LAST_VALUE() with a full frame scans further and can be more expensive.

When many window functions share the same PARTITION BY and ORDER BY, most engines compute the ordering once and reuse it, so grouping related window calls together helps the optimiser.

Interview questions

What does FIRST_VALUE() return?

It returns the value of the given expression from the first row of the window frame, defined by the ORDER BY inside OVER. The same value is repeated on every row of the partition, so you can carry the top row onto all rows.

Why does FIRST_VALUE() need an ORDER BY?

Without ORDER BY the window has no defined order, so first has no meaning and the engine may return any row. Adding ORDER BY makes the result deterministic.

How is FIRST_VALUE() different from MIN() or MAX()?

MIN and MAX return an aggregated value and, when used with GROUP BY, collapse rows. FIRST_VALUE keeps every row and returns whatever value sits in the first ordered row, which may not be the minimum or maximum unless you order by that column.

SELECT category, product,
       FIRST_VALUE(product)
         OVER (PARTITION BY category ORDER BY units DESC) AS best_seller
FROM product_sales;

Why does LAST_VALUE() often not return what people expect, but FIRST_VALUE() does?

The default frame runs from the first row of the partition to the current row. FIRST_VALUE reads the first row, which is always in that frame, so it works. LAST_VALUE() reads the last row of the frame, which is the current row by default, so it needs an explicit frame of ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.

Which databases support FIRST_VALUE()?

PostgreSQL and SQL Server have supported it for a long time. MySQL added it in version 8.0 and SQLite added it in version 3.25, both alongside the wider set of window functions.

Master SQL, one function at a time

Browse the full SQL functions library, or learn the fundamentals with our free, structured courses.