Windows I — ranking & offsets
Concept  ·  SQL & Querying

NTILE(n) deals rows into n buckets as evenly as it can — when counts do not divide, the leading buckets get the extra rows, so labels sit on unequal populations.

NTILE answers 'deal these rows into n hands' — a row-count operation, not a value-distribution one. Two consequences surprise: bucket sizes differ by one (extra rows go to the leading buckets — misleading when bucket 1's average is compared to bucket 3's on different denominators), and equal values can land in different buckets because the deal cuts by position. For value thresholds, use percentiles or explicit range buckets.

See it break
castings
idw
110
220
330
430
550
660
770
The trap
SELECT
  w,
  NTILE (3) OVER (
    ORDER BY
      w,
      id
  ) AS bucket
FROM
  castings
ORDER BY
  id;
The fix
SELECT
  w,
  CASE
    WHEN w < 40 THEN 1
    WHEN w < 70 THEN 2
    ELSE 3
  END AS bucket
FROM
  castings
ORDER BY
  id;
INPUTYour queryThe fix
1011
2011
3011
3021
5022
6032
7033

NTILE put the two equal 30-weight castings in different buckets — it deals rows by position, not value, and over 7 rows the buckets are 3·2·2; a value-range CASE keeps equal weights together.

The rule

NTILE for row-splits (sizes acknowledged); PERCENT_RANK/percentiles or CASE ranges for value semantics; a deterministic ORDER BY under all.

Shows up elsewhere
ROW_NUMBER vs RANK vs DENSE_RANKCASE takes the first match
Try one →Prove it in practice →