runtime
46 / 88

lesson 46 Window functions · advanced

NTILE & balanced ranking buckets

Split ordered rows into ranking buckets.

the idea

NTILE(n) splits the ordered rows of a window into n buckets of as equal size as possible and returns each row’s bucket number. Use it for quartiles or deciles of a population, ordering by the measure and a tie-breaker.

Dataset: Sports

The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.

games13 columns · 23,168 rows
game_idtext · unique idseasonnumber · links to seasonsgame_datedategame_numbernumbergame_typetextvenue_idtext · links to venueshome_team_idtext · links to teamsaway_team_idtext · links to teamshome_scorenumberaway_scorenumberday_nighttextattendancenumberduration_minutesnumber
ANA20160404020162016-04-040regularANA01ANACHN09night44020188
ANA20160405020162016-04-050regularANA01ANACHN16night37042154
ANA20160407020162016-04-070regularANA01ANATEX43night39089191
ANA20160408020162016-04-080regularANA01ANATEX37night35207210
ANA20160409020162016-04-090regularANA01ANATEX14night38106173
ANA20160410020162016-04-100regularANA01ANATEX31day35097163
game_idtext · unique id
ANA201604040
seasonnumber · links to seasons
2016
game_datedate
2016-04-04
game_numbernumber
0
game_typetext
regular
venue_idtext · links to venues
ANA01
home_team_idtext · links to teams
ANA
away_team_idtext · links to teams
CHN
home_scorenumber
0
away_scorenumber
9
day_nighttext
night
attendancenumber
44020
duration_minutesnumber
188
game_idtext · unique id
ANA201604050
seasonnumber · links to seasons
2016
game_datedate
2016-04-05
game_numbernumber
0
game_typetext
regular
venue_idtext · links to venues
ANA01
home_team_idtext · links to teams
ANA
away_team_idtext · links to teams
CHN
home_scorenumber
1
away_scorenumber
6
day_nighttext
night
attendancenumber
37042
duration_minutesnumber
154
game_idtext · unique id
ANA201604070
seasonnumber · links to seasons
2016
game_datedate
2016-04-07
game_numbernumber
0
game_typetext
regular
venue_idtext · links to venues
ANA01
home_team_idtext · links to teams
ANA
away_team_idtext · links to teams
TEX
home_scorenumber
4
away_scorenumber
3
day_nighttext
night
attendancenumber
39089
duration_minutesnumber
191
game_idtext · unique id
ANA201604080
seasonnumber · links to seasons
2016
game_datedate
2016-04-08
game_numbernumber
0
game_typetext
regular
venue_idtext · links to venues
ANA01
home_team_idtext · links to teams
ANA
away_team_idtext · links to teams
TEX
home_scorenumber
3
away_scorenumber
7
day_nighttext
night
attendancenumber
35207
duration_minutesnumber
210
game_idtext · unique id
ANA201604090
seasonnumber · links to seasons
2016
game_datedate
2016-04-09
game_numbernumber
0
game_typetext
regular
venue_idtext · links to venues
ANA01
home_team_idtext · links to teams
ANA
away_team_idtext · links to teams
TEX
home_scorenumber
1
away_scorenumber
4
day_nighttext
night
attendancenumber
38106
duration_minutesnumber
173
game_idtext · unique id
ANA201604100
seasonnumber · links to seasons
2016
game_datedate
2016-04-10
game_numbernumber
0
game_typetext
regular
venue_idtext · links to venues
ANA01
home_team_idtext · links to teams
ANA
away_team_idtext · links to teams
TEX
home_scorenumber
3
away_scorenumber
1
day_nighttext
day
attendancenumber
35097
duration_minutesnumber
163

first 6 of 23,168 rows · your queries use all of them

player_game_stats17 columns · 73,029 rows
game_idtext · links to gamesplayer_idtext · links to playersteam_idtext · links to teamsbatting_ordernumberstartednumberplate_appearancesnumberat_batsnumberrunsnumberhitsnumberdoublesnumbertriplesnumberhome_runsnumberrbinumberwalksnumberstrikeoutsnumberstolen_basesnumberhit_by_pitchnumber
ANA202504040adelj001ANA81550210030200
ANA202504040andet001ANA40110000000000
ANA202504040ariag002CLE81421100002000
ANA202504040clase001CLENULL0000000000000
ANA202504040danac001ANANULL0000000000000
ANA202504040darnt001ANA90110000000000
game_idtext · links to games
ANA202504040
player_idtext · links to players
adelj001
team_idtext · links to teams
ANA
batting_ordernumber
8
startednumber
1
plate_appearancesnumber
5
at_batsnumber
5
runsnumber
0
hitsnumber
2
doublesnumber
1
triplesnumber
0
home_runsnumber
0
rbinumber
3
walksnumber
0
strikeoutsnumber
2
stolen_basesnumber
0
hit_by_pitchnumber
0
game_idtext · links to games
ANA202504040
player_idtext · links to players
andet001
team_idtext · links to teams
ANA
batting_ordernumber
4
startednumber
0
plate_appearancesnumber
1
at_batsnumber
1
runsnumber
0
hitsnumber
0
doublesnumber
0
triplesnumber
0
home_runsnumber
0
rbinumber
0
walksnumber
0
strikeoutsnumber
0
stolen_basesnumber
0
hit_by_pitchnumber
0
game_idtext · links to games
ANA202504040
player_idtext · links to players
ariag002
team_idtext · links to teams
CLE
batting_ordernumber
8
startednumber
1
plate_appearancesnumber
4
at_batsnumber
2
runsnumber
1
hitsnumber
1
doublesnumber
0
triplesnumber
0
home_runsnumber
0
rbinumber
0
walksnumber
2
strikeoutsnumber
0
stolen_basesnumber
0
hit_by_pitchnumber
0
game_idtext · links to games
ANA202504040
player_idtext · links to players
clase001
team_idtext · links to teams
CLE
batting_ordernumber
NULL
startednumber
0
plate_appearancesnumber
0
at_batsnumber
0
runsnumber
0
hitsnumber
0
doublesnumber
0
triplesnumber
0
home_runsnumber
0
rbinumber
0
walksnumber
0
strikeoutsnumber
0
stolen_basesnumber
0
hit_by_pitchnumber
0
game_idtext · links to games
ANA202504040
player_idtext · links to players
danac001
team_idtext · links to teams
ANA
batting_ordernumber
NULL
startednumber
0
plate_appearancesnumber
0
at_batsnumber
0
runsnumber
0
hitsnumber
0
doublesnumber
0
triplesnumber
0
home_runsnumber
0
rbinumber
0
walksnumber
0
strikeoutsnumber
0
stolen_basesnumber
0
hit_by_pitchnumber
0
game_idtext · links to games
ANA202504040
player_idtext · links to players
darnt001
team_idtext · links to teams
ANA
batting_ordernumber
9
startednumber
0
plate_appearancesnumber
1
at_batsnumber
1
runsnumber
0
hitsnumber
0
doublesnumber
0
triplesnumber
0
home_runsnumber
0
rbinumber
0
walksnumber
0
strikeoutsnumber
0
stolen_basesnumber
0
hit_by_pitchnumber
0

first 6 of 73,029 rows · your queries use all of them

teams6 columns · 31 rows
team_idtext · unique idfranchise_idtextcitytextnicknametextleaguetextdivisiontext
ANAANAAnaheimAngelsALWest
ARIARIArizonaDiamondbacksNLWest
ATHOAKSacramentoAthleticsALWest
ATLATLAtlantaBravesNLEast
BALBALBaltimoreOriolesALEast
BOSBOSBostonRed SoxALEast
team_idtext · unique id
ANA
franchise_idtext
ANA
citytext
Anaheim
nicknametext
Angels
leaguetext
AL
divisiontext
West
team_idtext · unique id
ARI
franchise_idtext
ARI
citytext
Arizona
nicknametext
Diamondbacks
leaguetext
NL
divisiontext
West
team_idtext · unique id
ATH
franchise_idtext
OAK
citytext
Sacramento
nicknametext
Athletics
leaguetext
AL
divisiontext
West
team_idtext · unique id
ATL
franchise_idtext
ATL
citytext
Atlanta
nicknametext
Braves
leaguetext
NL
divisiontext
East
team_idtext · unique id
BAL
franchise_idtext
BAL
citytext
Baltimore
nicknametext
Orioles
leaguetext
AL
divisiontext
East
team_idtext · unique id
BOS
franchise_idtext
BOS
citytext
Boston
nicknametext
Red Sox
leaguetext
AL
divisiontext
East

first 6 of 31 rows · your queries use all of them

worked example

Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.

  1. Split teams into three buckets. Run this. NTILE(3) sorts teams by wins and labels each with tier 1, 2, or 3—roughly equal-sized groups for comparing standings.the pattern

    example.sql
    SELECT
      team_id,
      wins,
      NTILE(3) OVER (ORDER BY wins DESC, team_id) AS tier
    FROM standings
    WHERE season = 2025 AND as_of_date = (
      SELECT last_regular_date
      FROM seasons
      WHERE season = 2025
    );
  2. Preview qualified batting averages. Run this. Only players with at least 400 plate appearances appear—the pool you will quartile in the tasks.

    step-2.sql
    SELECT
      s.player_id,
      SUM(s.plate_appearances) AS pa,
      ROUND(1.0 * SUM(s.hits) / SUM(s.at_bats), 3) AS batting_avg
    FROM player_game_stats s
    JOIN games g ON g.game_id = s.game_id
    WHERE g.game_type = 'regular'
    GROUP BY s.player_id
    HAVING SUM(s.plate_appearances) >= 400
    ORDER BY batting_avg DESC
    LIMIT 10;

common mistake

NTILE balances counts, not values: tied batting averages can land in different quartiles, so a deterministic tie-breaker matters.

challenge 1

For players with at least 400 plate appearances in the 2025 regular season, return player_id, batting_avg (hits ÷ at bats, rounded to 3 decimals) and quartile: NTILE(4) ordered by batting_avg descending, then player_id.

what to return

Columns
player_idbatting_avgquartile in this order
Rows
any order
challenge-1.sql
 
To master this lesson, solve both challenges without hints and pass the concept check. 3 of 3 to go.
NTILE & balanced ranking buckets — Window functions · Runtime