想象一下桌上的运动员。它有一个名字,一个跳高得分和一个跳远得分。
CREATE TABLE athletes (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
high_jump_score DECIMAL,
long_jump_score DECIMAL
);
INSERT INTO athletes (name, high_jump_score, long_jump_score) VALUES
('Canga Roo', 2, 8),
('Bob Johnson', 1.90, 7.5),
('John Doe', 1.85, 7.2),
('Jane Smith', 1.75, 6.8),
('Alice Brown', 1.76, 6.7);
现在我想创建一个查询,交替列出最高的跳高运动员,然后是最高的跳远运动员(除非他们是同一个人,在这种情况下是第二好的跳远运动员),然后是第二高的跳高运动员和很快。每个运动员都会以各自的最高排名出现一次。
这样想。你面前有一份运动员名单,上面有他们的分数和一张白纸。你从列表中选择最好的跳投者,首先将他写在纸上,然后将他从列表中划掉。现在你选择最好的跳远运动员,然后从名单中越过他。现在你选择下一个最好的跳投并把他划掉。这样做直到所有运动员都被划掉。
让这个问题变得棘手的是避免双打。正确的解决方案是:
Canga Roo; 1st Hi-Jump
Bob Johnson; 1st Long-Jump
John Doe; 2nd Hi-Jump
Alice Brown; 2nd Long-Jump
Jame Smit; 3rd Hi-jump
我尝试先通过Hi-Jump对它们进行排名排序,然后进行Long-Jump排序,然后在Hi-Rank和Long-Rank之间选择各自的最高排名(f-rank),根据较高的排名分配一个组,最后排序按 f 排名第一,组第二:
WITH RankedAthletes AS (
SELECT
name,
high_jump_score,
long_jump_score,
ROW_NUMBER() OVER (ORDER BY high_jump_score DESC) AS hi_rank,
ROW_NUMBER() OVER (ORDER BY long_jump_score DESC) AS long_rank
FROM athletes
)
, FinalRanking AS (
SELECT
name,
hi_rank,
long_rank,
LEAST(hi_rank,long_rank) as f_rank,
( CASE
WHEN hi_rank < long_rank THEN 'Hi-Jump'
ELSE 'Long-Jump'
END ) AS "group"
FROM RankedAthletes
)
SELECT
name,
hi_rank,
long_rank,
f_rank,
"group",
ROW_NUMBER() OVER (PARTITION BY "group" ORDER BY f_rank) final_rank
FROM FinalRanking
ORDER BY final_rank, "group";
不幸的是,最终爱丽丝·布朗(Alice Brown)排名第一,因为她是唯一一个跳高排名与跳远排名不同的人,所以她是第 0 组中唯一的一个,所以她排名第一。
有没有办法解决这个问题,以便正确地将人员分配到组中?
编辑:
第二个例子更好地展示问题:
INSERT INTO athletes (name, high_jump_score, long_jump_score) VALUES
('Canga Roo', 10, 10),
('Bob Johnson', 1, 5),
('John Doe', 2, 4),
('Jane Smith', 3, 3),
('Alice Brown', 4, 2);
想要的结果
Canga Roo, 1st Hi Jump
Bob Johnson, 1st Long Jump
Alice Brown, 2nd Hi Jump
John Doe, 2nd Long Jump
Jane Smith, 1st Hi Jump
union all
将它们全部列出两次,使它们的跳远/跳高排名最终位于同一列中,然后进行排序,并通过添加 boolean
说明哪个是哪个来决定哪个跳跃先出现。 lead()
可以告诉您先前列出的名称是否与当前列出的名称相同。 distinct on
可以确保每个人最多被列出一次,处于最高位置,此时您不再需要与lead()
进行比较。 db<>fiddle 的演示:
with ranked1 as (
select
name,
high_jump_score,
long_jump_score,
row_number() over (order by long_jump_score desc) as rank1,
true as is_long_jump_rank
from athletes
union all
select
name,
high_jump_score,
long_jump_score,
row_number() over (order by high_jump_score desc) as rank1,
false as is_long_jump_rank
from athletes )
,ranked2 as (
select distinct on (name) *,
row_number()over (order by rank1, is_long_jump_rank) as rank2
from ranked1
order by name,rank2)
select name,
high_jump_score,
long_jump_score,
row_number()over(order by rank2) as final_rank
from ranked2
order by final_rank;
名字 | 高跳分数 | 长跳得分 | 最终排名 |
---|---|---|---|
坎加罗奥 | 2 | 8 | 1 |
鲍勃·约翰逊 | 1.90 | 7.5 | 2 |
约翰·多伊 | 1.85 | 7.2 | 3 |
爱丽丝·布朗 | 1.76 | 6.7 | 4 |
简·史密斯 | 1.75 | 6.8 | 5 |