Advanced Window Functions: Time Travel with Your Data! ๐
Imagine youโre watching a parade from your bedroom window. You can see whoโs passing by right now, but what if you could also peek at who just passed or whoโs about to come? Thatโs exactly what advanced window functions let you do with your data!
The Magic Window Analogy ๐ช
Think of your database table as a long parade of numbers marching past your window. Regular SQL only shows you whoโs at your window RIGHT NOW. But with window functions, you get magical binoculars that let you:
- ๐ Look BACK at who just passed (LAG)
- ๐ Look FORWARD at whoโs coming next (LEAD)
- ๐ Set BOUNDARIES for how far you can see (Window Frame)
- โ Keep a RUNNING SCORE of everything (Running Totals)
- ๐ Get SUMMARIES of what you see (Aggregate Windows)
1. LAG Function: Peeking at the Past ๐
What is LAG?
LAG is like having eyes in the back of your head. It lets you see the value from a previous row while staying on your current row.
Simple Example: Yesterdayโs Temperature
Imagine youโre tracking daily temperatures:
SELECT
day_name,
temperature,
LAG(temperature, 1)
OVER (ORDER BY day_date)
AS yesterday_temp
FROM weather;
Result:
| day_name | temperature | yesterday_temp |
|---|---|---|
| Monday | 72 | NULL |
| Tuesday | 75 | 72 |
| Wednesday | 68 | 75 |
How LAG Works
LAG(column, offset, default)
โ โ โ
โ โ โโโ What to show if no previous row (optional)
โ โโโโโโโโโโโ How many rows back to look (default: 1)
โโโโโโโโโโโโโโโโโโโโ Which column to peek at
Real-Life Use: Price Changes
SELECT
product_name,
price,
LAG(price) OVER (
PARTITION BY product_name
ORDER BY change_date
) AS old_price,
price - LAG(price) OVER (
PARTITION BY product_name
ORDER BY change_date
) AS price_change
FROM prices;
๐ฏ Key Insight: LAG looks BACKWARDS. Think โLAG = Look At past Guyโ
2. LEAD Function: Seeing the Future ๐
What is LEAD?
LEAD is LAGโs twin sister, but she looks forward instead of backward. She tells you whatโs coming NEXT!
Simple Example: Tomorrowโs Weather
SELECT
day_name,
temperature,
LEAD(temperature, 1)
OVER (ORDER BY day_date)
AS tomorrow_temp
FROM weather;
Result:
| day_name | temperature | tomorrow_temp |
|---|---|---|
| Monday | 72 | 75 |
| Tuesday | 75 | 68 |
| Wednesday | 68 | NULL |
The LEAD Pattern
LEAD(column, offset, default)
โ โ โ
โ โ โโโ What to show if no future row
โ โโโโโโโโโโโ How many rows ahead to look
โโโโโโโโโโโโโโโโโโโโ Which column to peek at
Real-Life Use: Appointment Gaps
SELECT
appointment_time,
patient_name,
LEAD(appointment_time) OVER (
ORDER BY appointment_time
) AS next_appointment,
TIMESTAMPDIFF(
MINUTE,
appointment_time,
LEAD(appointment_time) OVER (
ORDER BY appointment_time
)
) AS gap_minutes
FROM appointments;
๐ฏ Key Insight: LEAD looks FORWARD. Think โLEAD = Look Eager to Ahead Dataโ
3. Window Frame Clauses: Setting Your View ๐
What is a Window Frame?
Remember our parade analogy? A window frame decides exactly how much of the parade you can see through your magic window!
The Building Blocks
OVER (
PARTITION BY category -- Which parade?
ORDER BY date -- Which direction?
ROWS BETWEEN ... AND ... -- How wide is window?
)
Frame Types: ROWS vs RANGE
ROWS = Count by position (1st, 2nd, 3rd...)
RANGE = Count by value (same values = same group)
Frame Boundaries Explained
UNBOUNDED PRECEDING โ From the very beginning
N PRECEDING โ From N rows back
CURRENT ROW โ Right here
N FOLLOWING โ To N rows ahead
UNBOUNDED FOLLOWING โ To the very end
Visual: Frame Examples
Data: [10] [20] [30] [40] [50]
โฒ (Current Row)
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW:
[20] [30]
โโโโฌโโโ
Window
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING:
[20] [30] [40]
โโโโโโฌโโโโโ
Window
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:
[10] [20] [30]
โโโโโโฌโโโโโ
Window (all from start to here)
Practical Example: 3-Day Moving Average
SELECT
sale_date,
revenue,
AVG(revenue) OVER (
ORDER BY sale_date
ROWS BETWEEN 2 PRECEDING
AND CURRENT ROW
) AS three_day_avg
FROM sales;
4. Running Totals and Averages: The Scoreboard ๐
What are Running Totals?
Imagine keeping score in a basketball game. After each basket, you ADD to the total. You donโt start overโyou keep a running score!
The Classic Running Total
SELECT
order_date,
order_amount,
SUM(order_amount) OVER (
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS running_total
FROM orders;
Result:
| order_date | order_amount | running_total |
|---|---|---|
| Jan 1 | 100 | 100 |
| Jan 2 | 50 | 150 |
| Jan 3 | 75 | 225 |
Running Average: The Moving Lens
SELECT
test_date,
score,
AVG(score) OVER (
ORDER BY test_date
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) AS running_avg
FROM test_scores;
Grouped Running Totals
Want separate running totals per category? Use PARTITION BY:
SELECT
category,
sale_date,
amount,
SUM(amount) OVER (
PARTITION BY category
ORDER BY sale_date
ROWS UNBOUNDED PRECEDING
) AS category_running_total
FROM sales;
graph TD A["Each Row"] --> B{PARTITION BY?} B -->|Yes| C["Separate Running Total<br>per Group"] B -->|No| D["One Running Total<br>for All"] C --> E["ORDER BY determines<br>accumulation order"] D --> E
5. Aggregate Window Functions: The Birdโs Eye View ๐ฆ
What Makes Them Special?
Regular aggregates (SUM, AVG, COUNT) collapse many rows into one. Window aggregates give you the summary while keeping every row!
Compare: Regular vs Window
Regular Aggregate:
SELECT category, SUM(price)
FROM products
GROUP BY category;
-- Returns ONE row per category
Window Aggregate:
SELECT
product_name,
category,
price,
SUM(price) OVER (
PARTITION BY category
) AS category_total
FROM products;
-- Returns EVERY row, plus the total!
The Power Trio: SUM, AVG, COUNT as Windows
SELECT
employee_name,
department,
salary,
-- Total salary in department
SUM(salary) OVER (
PARTITION BY department
) AS dept_total,
-- Average salary in department
AVG(salary) OVER (
PARTITION BY department
) AS dept_avg,
-- How many in department
COUNT(*) OVER (
PARTITION BY department
) AS dept_count,
-- Your salary as % of department
ROUND(100.0 * salary / SUM(salary)
OVER (PARTITION BY department), 1
) AS pct_of_dept
FROM employees;
MIN and MAX Windows
SELECT
product_name,
price,
MIN(price) OVER () AS cheapest,
MAX(price) OVER () AS priciest,
price - MIN(price) OVER ()
AS above_minimum
FROM products;
Putting It All Together: A Complete Example ๐ช
Letโs analyze a storeโs daily sales with ALL our tools:
SELECT
sale_date,
daily_revenue,
-- LAG: Yesterday's revenue
LAG(daily_revenue) OVER w
AS yesterday,
-- LEAD: Tomorrow's revenue
LEAD(daily_revenue) OVER w
AS tomorrow,
-- Running Total
SUM(daily_revenue) OVER (
ORDER BY sale_date
ROWS UNBOUNDED PRECEDING
) AS running_total,
-- 3-Day Moving Average
AVG(daily_revenue) OVER (
ORDER BY sale_date
ROWS BETWEEN 2 PRECEDING
AND CURRENT ROW
) AS three_day_avg,
-- Comparison to overall average
daily_revenue - AVG(daily_revenue)
OVER () AS vs_overall_avg
FROM daily_sales
WINDOW w AS (ORDER BY sale_date);
Quick Reference: The Cheat Codes ๐ฎ
| Function | What It Does | Memory Trick |
|---|---|---|
| LAG(col, n) | Look n rows BACK | Look At past Guy |
| LEAD(col, n) | Look n rows AHEAD | Lead the way forward |
| ROWS BETWEEN | Set exact boundaries | Like setting fence posts |
| UNBOUNDED PRECEDING | From the very start | โSince the beginning of timeโ |
| Running SUM | Accumulating total | Basketball scoreboard |
| Window AVG | Average without collapsing | Birdโs eye view |
You Did It! ๐
Youโve just learned the time-travel powers of SQL! Now you can:
- โ Peek at previous values with LAG
- โ See future values with LEAD
- โ Control your view with Window Frames
- โ Keep running scores with Running Totals
- โ Get summaries while keeping details with Aggregate Windows
These arenโt just fancy tricksโtheyโre essential tools for real data analysis. Stock analysts use them for moving averages. Business analysts use them for trend analysis. And now, YOU can use them too!
๐ก Final Tip: Practice with small datasets first. Once you see the pattern, these functions become second nature!
