📖 Scenario: You work for a retail company that tracks sales data. You want to find the first and last sale dates for each product to understand sales trends.
🎯 Goal: Create a SQL query that uses FIRST_VALUE and LAST_VALUE window functions to find the first and last sale dates for each product.
📋 What You'll Learn
Create a table called sales with columns product_id (integer), sale_date (date), and amount (integer).
Insert the exact sales data provided.
Write a SQL query that selects product_id, sale_date, amount, the first sale date per product using FIRST_VALUE(sale_date), and the last sale date per product using LAST_VALUE(sale_date).
Use window functions partitioned by product_id and ordered by sale_date.
Ensure the LAST_VALUE function uses the correct frame clause to get the last sale date per product.
💡 Why This Matters
🌍 Real World
Retail companies analyze sales data to understand when products first and last sold, helping with inventory and marketing decisions.
💼 Career
Knowing how to use window functions like FIRST_VALUE and LAST_VALUE is important for data analysts and database developers to write advanced SQL queries.
Progress0 / 4 steps
1
Create the sales table and insert data
Create a table called sales with columns product_id (integer), sale_date (date), and amount (integer). Then insert these exact rows: (1, '2024-01-01', 100), (1, '2024-01-05', 150), (2, '2024-01-03', 200), (2, '2024-01-10', 250).
SQL
Hint
Use CREATE TABLE to define the table and INSERT INTO to add rows.
2
Set up the window partition and order
Write a SQL query selecting product_id, sale_date, and amount from sales. Add a window function partitioned by product_id and ordered by sale_date. Assign this window to an alias called win.
SQL
Hint
Use the WINDOW clause to define a window named win partitioned by product_id and ordered by sale_date.
3
Add FIRST_VALUE and LAST_VALUE columns
Extend the SELECT query to include FIRST_VALUE(sale_date) OVER win as first_sale_date and LAST_VALUE(sale_date) OVER win as last_sale_date. Use the window named win.
SQL
Hint
Use FIRST_VALUE(sale_date) OVER win and LAST_VALUE(sale_date) OVER win in the SELECT list.
4
Fix LAST_VALUE frame to get correct last sale date
Modify the LAST_VALUE(sale_date) function to include ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING frame clause inside the OVER(win ...) to ensure it returns the last sale date per product correctly.
SQL
Hint
Use the frame clause ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING inside the OVER for LAST_VALUE.
Practice
(1/5)
1. What does the SQL function FIRST_VALUE(column_name) OVER (ORDER BY column_name) return?
easy
A. The average value of the column
B. The first value in the ordered set of rows for that column
C. The last value in the ordered set of rows for that column
D. The total count of rows in the table
Solution
Step 1: Understand FIRST_VALUE function
It returns the first value in the ordered set of rows defined by the OVER clause.
Step 2: Analyze the ORDER BY clause
The ORDER BY inside OVER() defines the order to pick the first value from.
Final Answer:
The first value in the ordered set of rows for that column -> Option B
Quick Check:
FIRST_VALUE returns first row value [OK]
Hint: FIRST_VALUE picks the first row after ordering [OK]
Common Mistakes:
Confusing FIRST_VALUE with LAST_VALUE
Thinking it returns an aggregate like average
Ignoring the ORDER BY inside OVER()
2. Which of the following is the correct syntax to get the last value of salary ordered by hire_date in SQL?
easy
A. SELECT LAST_VALUE(salary) OVER (ORDER BY hire_date) FROM employees;
B. SELECT LAST_VALUE(salary) FROM employees ORDER BY hire_date;
C. SELECT LAST_VALUE(salary) OVER (PARTITION BY hire_date) FROM employees;
D. SELECT LAST_VALUE(salary) OVER (ORDER BY hire_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) FROM employees;
Solution
Step 1: Understand LAST_VALUE default frame
LAST_VALUE by default looks only at rows up to current row, so it may not return the true last value.
Step 2: Use frame clause to include all rows
Adding ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ensures LAST_VALUE looks at the entire partition.
Final Answer:
SELECT LAST_VALUE(salary) OVER (ORDER BY hire_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) FROM employees; -> Option D
Quick Check:
LAST_VALUE needs full frame to get last row [OK]
Hint: Add full frame clause to LAST_VALUE for correct last row [OK]
Common Mistakes:
Omitting frame clause with LAST_VALUE
Using PARTITION BY incorrectly
Trying to use LAST_VALUE without OVER()
3. Given the table sales with columns region and amount, what will this query return?
SELECT region, amount, FIRST_VALUE(amount) OVER (PARTITION BY region ORDER BY amount DESC) AS max_amount FROM sales;
medium
A. The first amount in the whole table regardless of region
B. The lowest amount per region repeated on each row of that region
C. The highest amount per region repeated on each row of that region
D. Syntax error due to missing frame clause
Solution
Step 1: Analyze PARTITION BY and ORDER BY
The window partitions rows by region and orders amounts descending within each region.
Step 2: Understand FIRST_VALUE with ordering
FIRST_VALUE picks the first row's amount in each partition ordered by amount DESC, which is the max amount.
Final Answer:
The highest amount per region repeated on each row of that region -> Option C
Quick Check:
FIRST_VALUE with DESC order gives max per partition [OK]
Hint: Use ORDER BY DESC with FIRST_VALUE for max per group [OK]
Common Mistakes:
Assuming FIRST_VALUE always returns lowest value
Confusing PARTITION BY with filtering
Expecting syntax error without frame clause here
4. Identify the error in this query:
SELECT employee_id, LAST_VALUE(salary) OVER (ORDER BY employee_id) AS last_sal FROM employees;
medium
A. LAST_VALUE needs a frame clause to get correct last value
B. Missing PARTITION BY clause causes error
C. ORDER BY cannot be used inside OVER()
D. salary column must be aggregated with GROUP BY
Solution
Step 1: Check LAST_VALUE default behavior
By default, LAST_VALUE looks only up to current row, so it may not return the true last value.
Step 2: Add frame clause to fix
Adding ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING makes LAST_VALUE consider all rows.
Final Answer:
LAST_VALUE needs a frame clause to get correct last value -> Option A
Quick Check:
LAST_VALUE without frame returns wrong last row [OK]
Hint: Add full frame to LAST_VALUE for correct last row [OK]
Common Mistakes:
Thinking PARTITION BY is mandatory
Believing ORDER BY is invalid inside OVER()
Confusing LAST_VALUE with aggregate functions
5. You want to find the first and last sale amounts per customer ordered by sale date from the sales table. Which query correctly returns both values on each row?
hard
A. SELECT customer_id, sale_date, FIRST_VALUE(amount) OVER (PARTITION BY customer_id ORDER BY sale_date) AS first_sale, LAST_VALUE(amount) OVER (PARTITION BY customer_id ORDER BY sale_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_sale FROM sales;
B. SELECT customer_id, sale_date, FIRST_VALUE(amount) OVER (ORDER BY sale_date) AS first_sale, LAST_VALUE(amount) OVER (ORDER BY sale_date) AS last_sale FROM sales;
C. SELECT customer_id, sale_date, FIRST_VALUE(amount) AS first_sale, LAST_VALUE(amount) AS last_sale FROM sales GROUP BY customer_id;
D. SELECT customer_id, sale_date, MIN(amount) OVER (PARTITION BY customer_id) AS first_sale, MAX(amount) OVER (PARTITION BY customer_id) AS last_sale FROM sales;
Solution
Step 1: Use PARTITION BY customer_id
This groups rows by customer to get first and last sales per customer.
Step 2: Use ORDER BY sale_date for chronological order
Ordering by sale_date ensures first and last are by date.
Step 3: Add frame clause for LAST_VALUE
LAST_VALUE needs ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING to get true last value.
Final Answer:
Query in option A correctly returns first and last sale amounts per customer -> Option A
Quick Check:
FIRST_VALUE and LAST_VALUE with proper frame and partition [OK]
Hint: Use full frame for LAST_VALUE with PARTITION and ORDER BY [OK]
Common Mistakes:
Omitting frame clause for LAST_VALUE
Using aggregate functions instead of window functions
Not partitioning by customer_id
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.