00:00

MySQL first() Function

Last updated:

Some databases (and older tutorials) mention a FIRST() function that returns the first value of a column. MySQL has no such function: calling it is an error. That is a good thing, because "first" means nothing until you say first by what order. This tutorial shows the correct MySQL ways to get the first row, the first row per group and the first value in a window.

1. FIRST() Doesn't Exist in MySQL

query.sql
SELECT FIRST(amount) FROM orders;
Output
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(amount) FROM orders' at line 1

MySQL doesn't recognise FIRST(…) at all and reports a syntax error. (Microsoft Access has a FIRST() aggregate; standard SQL instead has the FIRST_VALUE() window function, which MySQL supports.)

First depends on an orderThe same five orders sorted two ways. Sorted by order date, the first row is order 101 from 1 March. Sorted by amount, the first row is order 104 with amount 120. A table has no first row until you choose an ORDER BY.ORDER BY ordered_oniddateamount101Mar 01950102Mar 02400103Mar 042300104Mar 05120105Mar 07610ORDER BY amountiddateamount104Mar 05120102Mar 02400105Mar 07610101Mar 01950103Mar 042300"first" only has a meaning once you say ORDER BY …
Figure 1: the first row changes with the sort order. Without ORDER BY there is no reliable "first".

2. First Row: ORDER BY … LIMIT 1

query.sql
SELECT order_id, customer, ordered_on, amount FROM orders ORDER BY ordered_on LIMIT 1;

Earliest order

order_idcustomerordered_onamount
101Anika2026-03-01950.00
query.sql
SELECT order_id, customer, amount FROM orders ORDER BY amount LIMIT 1;

Smallest order

order_idcustomeramount
104Chris120.00
Add a tie-breaker (for example ORDER BY ordered_on, order_id) so the result is the same every time, even when two rows share a date.

3. First Value Only: MIN()

If you only need the earliest date (not the whole row), MIN() is enough:

query.sql
SELECT MIN(ordered_on) AS first_order_date FROM orders;

Result: 1 row

first_order_date
2026-03-01
query.sql
SELECT customer, MIN(ordered_on) AS first_order FROM orders GROUP BY customer ORDER BY first_order;

Result: 3 rows

customerfirst_order
Anika2026-03-01
Bose2026-03-02
Chris2026-03-05

4. First Row per Group: ROW_NUMBER()

Each customer's first order, with all its details:

first-per-group.sql
SELECT customer, order_id, ordered_on, amount, channel
FROM (
  SELECT o.*,
         ROW_NUMBER() OVER (PARTITION BY customer ORDER BY ordered_on, order_id) AS rn
  FROM orders o
) t
WHERE rn = 1
ORDER BY customer;

Result: 3 rows

customerorder_idordered_onamountchannel
Anika1012026-03-01950.00web
Bose1022026-03-02400.00app
Chris1042026-03-05120.00web

The same with a CTE, which some find easier to read:

cte.sql
WITH ranked AS (
  SELECT o.*, ROW_NUMBER() OVER (PARTITION BY customer ORDER BY ordered_on) AS rn
  FROM orders o
)
SELECT customer, order_id, channel FROM ranked WHERE rn = 1;

Result: 3 rows

customerorder_idchannel
Anika101web
Bose102app
Chris104web

5. FIRST_VALUE() Window Function

FIRST_VALUE(expr) OVER (…) puts the first value of each window on every row, so you can compare each order with the customer's first one:

first-value.sql
SELECT customer, order_id, ordered_on, amount,
       FIRST_VALUE(amount)     OVER w AS first_amount,
       FIRST_VALUE(ordered_on) OVER w AS first_date,
       amount - FIRST_VALUE(amount) OVER w AS change_since_first
FROM orders
WINDOW w AS (PARTITION BY customer ORDER BY ordered_on)
ORDER BY customer, ordered_on;

Result: 7 rows

customerorder_idordered_onamountfirst_amountfirst_datechange_since_first
Anika1012026-03-01950.00950.002026-03-010.00
Anika1032026-03-042300.00950.002026-03-011350.00
Anika1072026-03-09300.00950.002026-03-01-650.00
Bose1022026-03-02400.00400.002026-03-020.00
Bose1052026-03-07610.00400.002026-03-02210.00
Chris1042026-03-05120.00120.002026-03-050.00
Chris1062026-03-08780.00120.002026-03-05660.00

6. Before MySQL 8: Join to a MIN Sub-query

Without window functions, find each group's first date and join back:

join-min.sql
SELECT o.customer, o.order_id, o.ordered_on
FROM orders o
JOIN (SELECT customer, MIN(ordered_on) AS first_date FROM orders GROUP BY customer) f
  ON f.customer = o.customer AND f.first_date = o.ordered_on
ORDER BY o.customer;

Result: 3 rows

customerorder_idordered_on
Anika1012026-03-01
Bose1022026-03-02
Chris1042026-03-05

7. Which Technique to Use

NeedUse
The first row overallORDER BY … LIMIT 1
Just the first/earliest valueMIN(col)
The first row in each groupROW_NUMBER() OVER (PARTITION BY … ORDER BY …) = 1
Each row compared with its group's first valueFIRST_VALUE(col) OVER (…)
The last row insteadSee MySQL last()

8. Summary

  • MySQL has no FIRST() aggregate; "first" always needs an ORDER BY.
  • First row: ORDER BY col LIMIT 1; first value: MIN(col).
  • First row per group: ROW_NUMBER() … WHERE rn = 1.
  • FIRST_VALUE() OVER (…) shows the first value next to every row.