BBampaa

Lessons › SQL › Deep Dive: Advanced Window Functions

SQL lesson

FIRST_VALUE() / LAST_VALUE() / NTH_VALUE()

These window functions (meaning they look across a set of related rows without collapsing them into one) let you reach into a ranked list and pull the value at a specific position - first, last, or anywhere in between - and stamp it alongside every row. Like bookmarking the opening chapter, the final page, and chapter two of every reader's history so you can compare them all at a glance.

Try this lesson free →
About 5–10 minutes · no sign-up needed

Example

Navigating Customer Order Values with FIRST_VALUE(), LAST_VALUE(), and NTH_VALUE()

SELECT order_id, customer_id, order_date, revenue, FIRST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS first_order_revenue, LAST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_revenue, NTH_VALUE(revenue, 2) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_order_revenue FROM orders ORDER BY customer_id, order_date

Result:

order_idcustomer_idrevenuefirst_order_revenuelast_order_revenuesecond_order_revenue
O2001C101320.0320.0540.085.5
O2002C10185.5320.0540.085.5
O2003C101540.0320.0540.085.5
O2004C10245.045.0130.75130.75
O2005C102130.7545.0130.75130.75

Step by step

  1. AS gives a column a friendlier name in the result.
    SELECT order_id, customer_id, order_date, revenue, FIRST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS first_order_revenue, LAST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_revenue, NTH_VALUE(revenue, 2) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_order_revenue
  2. FROM tells SQL which table to pull the data from.
    SELECT order_id, customer_id, order_date, revenue, FIRST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS first_order_revenue, LAST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_revenue, NTH_VALUE(revenue, 2) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_order_revenue
    FROM orders
  3. ORDER BY puts the rows in your chosen order.
    SELECT order_id, customer_id, order_date, revenue, FIRST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS first_order_revenue, LAST_VALUE(revenue) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_order_revenue, NTH_VALUE(revenue, 2) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_order_revenue
    FROM orders
    ORDER BY customer_id, order_date;

Practice

Try each one in your head first, then open the answer.

1. Using orders, write a query that returns the first revenue value within each category group, ordered by revenue.
SELECT *, FIRST_VALUE(revenue) OVER (PARTITION BY category ORDER BY revenue) FROM orders
2. Using store_sales, write a query that returns the last revenue value within each sale_id group, ordered by revenue.
SELECT *, LAST_VALUE(revenue) OVER (PARTITION BY sale_id ORDER BY revenue) FROM store_sales
3. Using products, write a query that returns the 3nd/3rd price value within each category group, ordered by price.
SELECT *, NTH_VALUE(price, 3) OVER (PARTITION BY category ORDER BY price) FROM products
4. Using employees, write a query that returns the first salary value within each full_name group, ordered by salary.
SELECT *, FIRST_VALUE(salary) OVER (PARTITION BY full_name ORDER BY salary) FROM employees
5. Using reviews, write a query that returns the last rating value within each review_id group, ordered by rating.
SELECT *, LAST_VALUE(rating) OVER (PARTITION BY review_id ORDER BY rating) FROM reviews

Learn it by doing. This lesson is a short hands-on level on Bampaa: see it work, then build and write it yourself, with instant feedback.

Try this lesson free →
About 5–10 minutes · no sign-up needed