Postgres 中的交替排名查询

问题描述 投票:0回答:1

想象一下桌上的运动员。它有一个名字,一个跳高得分和一个跳远得分。

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
postgresql window-functions ranking
1个回答
1
投票

您可以使用

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
© www.soinside.com 2019 - 2024. All rights reserved.