FIRST_VALUE and LAST_VALUE return a column from the first/last row of a window — e.g. each customer’s first and latest order amount, on every row.
SELECT
customer_id,
order_date,
amount,
FIRST_VALUE(amount) OVER w AS first_amount,
LAST_VALUE(amount) OVER w AS latest_amount
FROM orders
WINDOW w AS (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
);
LAST_VALUE needs the full frame. With just
ORDER BY, the default frame isRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, soLAST_VALUEreturns the current row, not the last one. AddROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING(as above) to see the whole partition.FIRST_VALUEis fine either way.
Dialect notes:
- The inline
WINDOW w AS (...)clause works in PostgreSQL, MySQL 8+, SQLite 3.25+, and DuckDB. BigQuery and Snowflake don’t support the namedWINDOWclause — inline theOVER (...)on each function instead. - A common alternative to
LAST_VALUEisFIRST_VALUE(... ORDER BY order_date DESC), which avoids the frame gotcha entirely.