After this lesson, you will be able to: Use window functions: ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, with OVER + PARTITION BY.
Window functions appear in nearly every data-engineering interview. They calculate values across a 'window' of rows without collapsing the result like GROUP BY does.
Number rows within partitions.
-- Rank posts within each user by created_atSELECT id, user_id, title, created_at,ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS user_rankFROM posts;-- Common pattern: 'most recent post per user'WITH ranked AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rnFROM posts)SELECT id, user_id, title, created_at FROM ranked WHERE rn = 1;-- RANK vs DENSE_RANK-- ROW_NUMBER: 1, 2, 3, 4 (always unique)-- RANK: 1, 1, 3, 4 (ties get same rank; next rank skips)-- DENSE_RANK: 1, 1, 2, 3 (ties get same rank; no skip)
Reference previous/next row in the window.
-- Daily revenue + previous-day revenue + day-over-day deltaSELECT day, revenue,LAG(revenue) OVER (ORDER BY day) AS prev_day,revenue - LAG(revenue) OVER (ORDER BY day) AS deltaFROM daily_revenue;-- LEAD looks aheadSELECT day, revenue, LEAD(revenue) OVER (ORDER BY day) AS next_dayFROM daily_revenue;
Use SUM/AVG with a window frame.
-- Cumulative revenueSELECT day, revenue,SUM(revenue) OVER (ORDER BY day) AS running_totalFROM daily_revenue;-- 7-day moving averageSELECT day, revenue,AVG(revenue) OVER (ORDER BY dayROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_7d_avgFROM daily_revenue;-- Partitioned running total per userSELECT user_id, day, amount,SUM(amount) OVER (PARTITION BY user_id ORDER BY day) AS running_totalFROM transactions;
Pick the right combination.
Forgetting ORDER BY inside OVER for ordered windows (results become indeterminate). Confusing PARTITION BY with GROUP BY (different scope). Not specifying window frame for SUM/AVG (defaults can surprise, RANGE vs ROWS). Window functions in WHERE (illegal, must wrap in CTE first).
Sign in and purchase access to unlock this lesson.