SQL Window Functions: Percentiles, Bucketing, and Distribution Analysis

Percent rank, cumulative distribution, percentile-style bucketing, and segmentation patterns.

View
StandardDetailedCompact
Export
Copy the compact sheet, download it, or print it.
Download
`D` dense toggle · `C` copy all

Distribution functions

Turn ordered values into percentile-like analytics.

Create deciles with NTILE(10)

Bucket rows into 10 groups.

sqlANYntiledeciles
sql
SELECT customer_id,
       spend,
       NTILE(10) OVER (ORDER BY spend DESC) AS spend_decile
FROM customers;
Notes

Great for segmentation, scoring, and rough percentile groupings.

Leaderboard percent rank

Relative standing from 0 to 1.

sqlANYpercent_rank
sql
SELECT player_id,
       score,
       PERCENT_RANK() OVER (ORDER BY score) AS pct_rank
FROM leaderboard;
Notes

A convenient way to label records as top 5%, top 10%, and so on.

Find the 90th-percentile style cutoff

Identify rows at or above a cumulative threshold.

sqlANYcume_distthreshold
sql
WITH scored AS (
  SELECT salary,
         CUME_DIST() OVER (ORDER BY salary) AS cd
  FROM employees
)
SELECT *
FROM scored
WHERE cd >= 0.90;
Notes

This is an intuitive pattern when exact percentile functions are unavailable or vary by dialect.

Segmentation patterns

Practical bucketing queries for product and revenue analytics.

Flag top quartile customers

Turn NTILE output into a simple segment label.

sqlANYsegmentation
sql
WITH bucketed AS (
  SELECT customer_id,
         lifetime_value,
         NTILE(4) OVER (ORDER BY lifetime_value DESC) AS quartile
  FROM customers
)
SELECT customer_id,
       lifetime_value,
       CASE WHEN quartile = 1 THEN 'top_quartile' ELSE 'other' END AS segment
FROM bucketed;
Notes

This is a simple but effective pattern for growth, CRM, and pricing analysis.

Build labeled score bands

Assign named score ranges using a window bucket.

sqlANYbands
sql
WITH scored AS (
  SELECT user_id,
         engagement_score,
         NTILE(5) OVER (ORDER BY engagement_score DESC) AS bucket
  FROM user_scores
)
SELECT user_id,
       engagement_score,
       CASE bucket
         WHEN 1 THEN 'elite'
         WHEN 2 THEN 'high'
         WHEN 3 THEN 'medium'
         WHEN 4 THEN 'low'
         ELSE 'very_low'
       END AS engagement_band
FROM scored;
Notes

Often easier to explain to stakeholders than raw percentiles.

Recommended next

No recommendations yet.