Working with Window Functions

1. Understanding Window Functions

vs AggregateWindow
Rows returnedOne per input row (no collapse)
Defined byOVER (PARTITION BY ... ORDER BY ...)
Use caseRanking, running totals, lag/lead

2. Using ROW_NUMBER Function

SELECT id, customer_id,
       row_number() OVER (PARTITION BY customer_id ORDER BY placed_at DESC) AS rn
  FROM orders;

3. Using RANK Function

SELECT name, score,
       rank() OVER (ORDER BY score DESC) AS rnk
  FROM players;
-- Ties share rank, next jumps (1,1,3)

4. Using DENSE_RANK Function

SELECT name, score, dense_rank() OVER (ORDER BY score DESC) FROM players;
-- (1,1,2)
FunctionTie Handling
row_numberUnique sequential
rankSame rank, gap after
dense_rankSame rank, no gap

5. Using NTILE Function

SELECT name, score, ntile(4) OVER (ORDER BY score DESC) AS quartile FROM players;

6. Using LAG Function

SELECT day, revenue,
       lag(revenue, 1) OVER (ORDER BY day) AS prev_day,
       revenue - lag(revenue, 1, 0) OVER (ORDER BY day) AS delta
  FROM daily_revenue;

7. Using LEAD Function

SELECT event_at, lead(event_at) OVER (PARTITION BY session_id ORDER BY event_at) AS next_event
  FROM events;

8. Using FIRST_VALUE Function

SELECT customer_id, placed_at,
       first_value(placed_at) OVER (PARTITION BY customer_id ORDER BY placed_at) AS first_order_at
  FROM orders;

9. Using LAST_VALUE Function

SELECT customer_id, placed_at,
       last_value(placed_at) OVER (
         PARTITION BY customer_id ORDER BY placed_at
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS last_order_at
  FROM orders;
Warning: Default window frame ends at CURRENT ROW—last_value needs explicit unbounded frame.

10. Using NTH_VALUE Function

SELECT customer_id,
       nth_value(placed_at, 2) OVER (
         PARTITION BY customer_id ORDER BY placed_at
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS second_order_at
  FROM orders;

11. Using PARTITION BY Clause

SELECT customer_id, total_cents,
       sum(total_cents) OVER (PARTITION BY customer_id) AS customer_total
  FROM orders;

12. Using ORDER BY in Window

EffectDetail
DefinesRow sequence within partition
Required forRunning totals, lag/lead, ranking
Default frameRANGE UNBOUNDED PRECEDING to CURRENT ROW

13. Defining Window Frame

SELECT day, revenue,
       avg(revenue) OVER (
         ORDER BY day ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
       ) AS rolling_7day_avg
  FROM daily_revenue;

14. Using Named Windows

SELECT id, customer_id,
       row_number() OVER w AS rn,
       sum(total_cents) OVER w AS running_total
  FROM orders
WINDOW w AS (PARTITION BY customer_id ORDER BY placed_at);

15. Using Aggregate Functions as Window Functions

SELECT day, revenue,
       sum(revenue) OVER (ORDER BY day) AS cumulative,
       100.0*revenue/sum(revenue) OVER () AS pct_of_total
  FROM daily_revenue;