Skip to main content

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;

Was this article helpful?