MySQL first() Function
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
SELECT FIRST(amount) FROM orders;
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.)
2. First Row: ORDER BY … LIMIT 1
SELECT order_id, customer, ordered_on, amount FROM orders ORDER BY ordered_on LIMIT 1;
Earliest order
| order_id | customer | ordered_on | amount |
|---|---|---|---|
| 101 | Anika | 2026-03-01 | 950.00 |
SELECT order_id, customer, amount FROM orders ORDER BY amount LIMIT 1;
Smallest order
| order_id | customer | amount |
|---|---|---|
| 104 | Chris | 120.00 |
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:
SELECT MIN(ordered_on) AS first_order_date FROM orders;
Result: 1 row
| first_order_date |
|---|
| 2026-03-01 |
SELECT customer, MIN(ordered_on) AS first_order FROM orders GROUP BY customer ORDER BY first_order;
Result: 3 rows
| customer | first_order |
|---|---|
| Anika | 2026-03-01 |
| Bose | 2026-03-02 |
| Chris | 2026-03-05 |
4. First Row per Group: ROW_NUMBER()
Each customer's first order, with all its details:
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
| customer | order_id | ordered_on | amount | channel |
|---|---|---|---|---|
| Anika | 101 | 2026-03-01 | 950.00 | web |
| Bose | 102 | 2026-03-02 | 400.00 | app |
| Chris | 104 | 2026-03-05 | 120.00 | web |
The same with a CTE, which some find easier to read:
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
| customer | order_id | channel |
|---|---|---|
| Anika | 101 | web |
| Bose | 102 | app |
| Chris | 104 | web |
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:
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
| customer | order_id | ordered_on | amount | first_amount | first_date | change_since_first |
|---|---|---|---|---|---|---|
| Anika | 101 | 2026-03-01 | 950.00 | 950.00 | 2026-03-01 | 0.00 |
| Anika | 103 | 2026-03-04 | 2300.00 | 950.00 | 2026-03-01 | 1350.00 |
| Anika | 107 | 2026-03-09 | 300.00 | 950.00 | 2026-03-01 | -650.00 |
| Bose | 102 | 2026-03-02 | 400.00 | 400.00 | 2026-03-02 | 0.00 |
| Bose | 105 | 2026-03-07 | 610.00 | 400.00 | 2026-03-02 | 210.00 |
| Chris | 104 | 2026-03-05 | 120.00 | 120.00 | 2026-03-05 | 0.00 |
| Chris | 106 | 2026-03-08 | 780.00 | 120.00 | 2026-03-05 | 660.00 |
6. Before MySQL 8: Join to a MIN Sub-query
Without window functions, find each group's first date and join back:
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
| customer | order_id | ordered_on |
|---|---|---|
| Anika | 101 | 2026-03-01 |
| Bose | 102 | 2026-03-02 |
| Chris | 104 | 2026-03-05 |
7. Which Technique to Use
| Need | Use |
|---|---|
| The first row overall | ORDER BY … LIMIT 1 |
| Just the first/earliest value | MIN(col) |
| The first row in each group | ROW_NUMBER() OVER (PARTITION BY … ORDER BY …) = 1 |
| Each row compared with its group's first value | FIRST_VALUE(col) OVER (…) |
| The last row instead | See 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.