Data Explorer
Ad hoc SQL queries against the full athlete dataset using DuckDB WASM.
Schema
Three tables are loaded into DuckDB:
athletes — all athletes combined (boys + girls)
| Column | Type | Description |
|---|---|---|
gender |
TEXT | 'Boys' or 'Girls' |
name |
TEXT | Full name |
first |
TEXT | First name |
last |
TEXT | Last name |
fs25_time |
DOUBLE | 2025 freestyle finish time (seconds) |
fs25_rank |
INTEGER | 2025 freestyle rank — full field including No Score |
cl25_time |
DOUBLE | 2025 classic finish time (seconds) |
cl25_rank |
INTEGER | 2025 classic rank — full field |
comb25_time |
DOUBLE | 2025 combined time: FS + classic (seconds); NULL if either race missing |
comb25_rank |
INTEGER | 2025 combined rank; NULL if either race missing |
fs26_time |
DOUBLE | 2026 freestyle finish time (seconds) |
fs26_rank |
INTEGER | 2026 freestyle rank — full field |
cl26_time |
DOUBLE | 2026 classic finish time (seconds) |
cl26_rank |
INTEGER | 2026 classic rank — full field |
comb26_time |
DOUBLE | 2026 combined time: FS + classic (seconds); NULL if either race missing |
comb26_rank |
INTEGER | 2026 combined rank; NULL if either race missing |
boys and girls — same schema as athletes minus the gender column.
Times are in seconds (float). To format as
M:SS.s:floor(t/60) || ':' || printf('%04.1f', t % 60).
Ad Hoc Query
Example Queries
Most improved — combined rank
Athletes who moved up the most places in combined ranking, broken out by gender.
SELECT
gender,
name,
comb25_rank AS rank_25,
comb26_rank AS rank_26,
comb25_rank - comb26_rank AS rank_change,
round(comb25_time - comb26_time) AS time_delta_s
FROM athletes
WHERE comb25_rank IS NOT NULL
AND comb26_rank IS NOT NULL
ORDER BY rank_change DESC
LIMIT 20
Freestyle time improvement
Raw time drop in the 5K freestyle, largest improvements first.
SELECT
gender,
name,
round(fs25_time) AS fs_25s,
round(fs26_time) AS fs_26s,
round(fs25_time - fs26_time) AS time_drop_s,
fs25_rank AS rank_25,
fs26_rank AS rank_26
FROM athletes
WHERE fs25_time IS NOT NULL
AND fs26_time IS NOT NULL
ORDER BY time_drop_s DESC
LIMIT 20
2026 leaderboard by gender and event
Top finishers in each event for 2026 with formatted times.
SELECT
gender,
comb26_rank AS combined_rank,
name,
floor(fs26_time/60) || ':' || printf('%04.1f', fs26_time % 60) AS freestyle,
floor(cl26_time/60) || ':' || printf('%04.1f', cl26_time % 60) AS classic,
floor(comb26_time/60) || ':' || printf('%04.1f', comb26_time % 60) AS combined
FROM athletes
WHERE comb26_rank IS NOT NULL
ORDER BY gender, comb26_rank
LIMIT 30
Athletes missing a combined rank
Athletes who finished at least one event in each year but are excluded from combined rankings — likely a DNS in one race.
SELECT
gender,
name,
fs25_rank, cl25_rank, comb25_rank,
fs26_rank, cl26_rank, comb26_rank
FROM athletes
WHERE (fs25_rank IS NOT NULL OR cl25_rank IS NOT NULL)
AND (fs26_rank IS NOT NULL OR cl26_rank IS NOT NULL)
AND (comb25_rank IS NULL OR comb26_rank IS NULL)
ORDER BY gender, name
Average times by event and year
Field-wide averages across both genders for each race and year.
SELECT
gender,
round(avg(fs25_time)) AS avg_fs_25s,
round(avg(fs26_time)) AS avg_fs_26s,
round(avg(cl25_time)) AS avg_cl_25s,
round(avg(cl26_time)) AS avg_cl_26s,
round(avg(comb25_time)) AS avg_comb_25s,
round(avg(comb26_time)) AS avg_comb_26s
FROM athletes
GROUP BY gender
ORDER BY gender
Gap to top finisher (SQL version of dumbbell chart)
Replicates the gap-to-reference logic in SQL. Swap the name in the ref CTE for any reference athlete.
WITH ref AS (
SELECT
comb25_time AS ref_25,
comb26_time AS ref_26
FROM athletes
WHERE name = 'Jayvin Lemieux'
AND gender = 'Boys'
)
SELECT
a.name,
round(a.comb25_time - r.ref_25) AS gap_25s,
round(a.comb26_time - r.ref_26) AS gap_26s,
round((a.comb25_time - r.ref_25) - (a.comb26_time - r.ref_26)) AS net_change_s
FROM athletes a, ref r
WHERE a.gender = 'Boys'
AND a.comb25_time IS NOT NULL
AND a.comb26_time IS NOT NULL
ORDER BY net_change_s DESC
LIMIT 20