Back to feed

Project docs

Ski Jumping Statistics

PostgreSQL analysis of men's Ski Jumping World Cup results, supported by a Python data collection pipeline.

Podiums

The number of first-, second-, and third-place finishes achieved by each athlete in individual World Cup competitions.

Rules

  • First, second, and third places are counted separately for each athlete.
  • The total value is the sum of all three podium positions.
  • Results can cover a selected season, a custom date range, or the complete dataset.

Podium finishes are taken from the competition_results materialized view, where athletes already have their final positions for each competition.

Function overloads

Aggregating several finishing positions for different periods would be inconvenient as a single view. Instead, the PL/pgSQL podiums() function uses overloading to support three variants through one consistent name:

  • two dates select a custom range,
  • a season string selects one season,
  • no arguments select the complete dataset.
create or replace function podiums(start_date date, end_date date)
returns table(
	jumper_id jumpers.jumper_id%type,
	firsts	bigint,
	seconds bigint,
	thirds bigint,
	total bigint
)
language plpgsql
as $$
begin
	return query
		with podiums_per_jumper as (
			select
				cr.jumper_id,
				sum(case when pos = 1 then 1 else 0 end) as firsts,
				sum(case when pos = 2 then 1 else 0 end) as seconds,
				sum(case when pos = 3 then 1 else 0 end) as thirds
			from competition_results cr
			join competitions c using (race_id)
			where start_date <= c.competition_started
				and end_date >= c.competition_started
			group by cr.jumper_id
		)
		select
			ppj.jumper_id,
			ppj.firsts,
			ppj.seconds,
			ppj.thirds,
			ppj.firsts + ppj.seconds + ppj.thirds as total
		from podiums_per_jumper ppj
		where (ppj.firsts + ppj.seconds + ppj.thirds) > 0;
end $$;

create or replace function podiums()
returns table(
	jumper_id jumpers.jumper_id%type,
	firsts	bigint,
	seconds bigint,
	thirds bigint,
	total bigint
)
language plpgsql
as $$
begin
	return query
		select *
		from podiums(make_date(1900, 1, 1), now()::date);
end $$;

create or replace function podiums(season char(9))
returns table(
	jumper_id jumpers.jumper_id%type,
	firsts	bigint,
	seconds bigint,
	thirds bigint,
	total bigint
)
language plpgsql
as $$
declare
	start_year int;
	end_year int;
begin
	start_year = split_part(season, '/', 1)::int;
	end_year = split_part(season, '/', 2)::int;

	if (end_year - start_year) != 1 then
		raise 'Argument % is not a valid season', season;
	end if;

	return query
		select *
		from podiums(
			make_date(start_year, 9, 1), 
			make_date(end_year, 5, 1)
		);
end $$;

Podiums in the 2022/2023 season

select
	j.jumper_name as jumper,
	p.firsts,
	p.seconds,
	p.thirds,
	p.total
from podiums('2022/2023') p
join jumpers j using (jumper_id)
order by p.firsts desc, p.seconds desc, p.thirds;

Results

Podiums in the 2022/2023 season17 rows
jumperfirstssecondsthirdstotal
GRANERUD Halvor Egner125118
KUBACKI Dawid64515
KRAFT Stefan56617
LANISEK Anze49316
KOBAYASHI Ryoyu3216
WELLINGER Andreas2103
ZAJC Timi1102
JELAR Ziga0101
FETTNER Manuel0112
ZYLA Piotr0134
PREVC Domen0011
LINDVIK Marius0011
EISENBICHLER Markus0011
TANDE Daniel Andre0011
NAKAMURA Naoki0011
TSCHOFENIG Daniel0033
GEIGER Karl0044

Podiums in the entire 2023

select
	j.jumper_name as jumper,
	p.firsts,
	p.seconds,
	p.thirds,
	p.total
from podiums('01-01-2023', '12-31-2023') p
join jumpers j using (jumper_id)
order by p.firsts desc, p.seconds desc, p.thirds;

Results

Podiums in 202320 rows
jumperfirstssecondsthirdstotal
GRANERUD Halvor Egner104115
KRAFT Stefan96621
KOBAYASHI Ryoyu3328
WELLINGER Andreas3328
KUBACKI Dawid2248
GEIGER Karl2035
LANISEK Anze17210
ZAJC Timi1102
PASCHKE Pius1113
HOERL Jan0213
JELAR Ziga0101
LINDVIK Marius0101
DESCHWANDEN Gregor0101
FETTNER Manuel0011
ZYLA Piotr0011
LEYHE Stephan0011
PREVC Domen0011
EISENBICHLER Markus0011
TANDE Daniel Andre0011
TSCHOFENIG Daniel0044

Total podiums

The no-argument overload returns all podiums recorded since the 2017/2018 season.

select
	j.jumper_name as jumper,
	p.firsts,
	p.seconds,
	p.thirds,
	p.total
from podiums() p
join jumpers j using (jumper_id)
order by p.firsts desc, p.seconds desc, p.thirds;

Results

All podiums since 2017/18 season55 rows
jumperfirstssecondsthirdstotal
KOBAYASHI Ryoyu37241879
KRAFT Stefan34263393
GRANERUD Halvor Egner2513341
PREVC Domen199634
STOCH Kamil1710936
GEIGER Karl15121441
LANISEK Anze11211143
KUBACKI Dawid1191838
TSCHOFENIG Daniel1151127
LINDVIK Marius961025
WELLINGER Andreas79925
PASCHKE Pius62210
HOERL Jan515828
FORFANG Johann Andre57618
TANDE Daniel Andre56617
ZAJC Timi54615
EISENBICHLER Markus3121025
FREITAG Richard3508
HUBER Daniel3328
JOHANSSON Robert33915
PREVC Peter26311
SATO Yukiya2114
KOS Lovro2114
ZYLA Piotr171220
NIKAIDO Ren1528
DESCHWANDEN Gregor1427
JELAR Ziga1304
EMBACHER Stephan1326
LEYHE Stephan1326
STJERNEN Andreas1214
RAIMUND Philipp1247
KLIMOV Evgeniy1102
NAITO Tomofumi1102
FANNEMEL Anders1113
SEMENIC Anze1001
KOBAYASHI Junshiro1001
DAMJAN Jernej1001
PEIER Killian0101
STEKALA Andrzej0101
ASCHENWALD Philipp0112
FETTNER Manuel0123
NAKAMURA Naoki0123
ORTNER Maximilian0123
PAVLOVCIC Bor0123
HOFFMANN Felix0134
SUNDAL Kristoffer Eriksen0134
HAYBOECK Michael0156
AMMANN Simon0011
SCHMID Constantin0011
ZOGRAFSKI Vladimir0011
SCHUSTER Jonas0011
PREVC Cene0011
AALTO Antti0011
WASEK Pawel0011
ZNISZCZOL Aleksander0022

Podiums by country

The same complete result can be aggregated by country instead of athlete.

select
	j.jumper_country as country,
	SUM(p.firsts) as firsts,
	SUM(p.seconds) as seconds,
	SUM(p.thirds) as thirds,
	SUM(p.total) as total
from podiums() p
join jumpers j using (jumper_id)
group by country
order by firsts desc, seconds desc, thirds;

Results

Podiums by country since 2017/18 season10 rows
countryfirstssecondsthirdstotal
AUT545667177
NOR493939127
SLO424530117
JPN42322397
GER364645127
POL29274298
SUI1539
RUS1102
FIN0011
BUL0011