问题描述
我有一个场景,需要根据总得分运行和每个年级进行计算。 以下是 2 名玩家的得分。
以下是需要申请的等级范围。总分是115,第一名玩家优先。所以计算从0-50,50-100和100-150。
因此 sachin 将有 70 次运行,其中 50 次将在 1 级以下,其余 20 次在 2 级中。 Dhoni 将从 70 开始,并在 2 年级以下的 45 到 100 中填充他的 30。 100 之后,Dhoni 剩下的 15 将被添加到 3 级。
寻找根据表日期 1 和 2 执行计算部分的查询。 表 3 是预期的查询输出。
解决方法
首先让我们设置数据。我从三个表开始:players
(每个玩家一行;这包括 ID、名称和优先级 - 优先级不应出现在“分数”表中); grades
就像在您的问题中一样(尽管它应该看起来更像是我在下面的 g
子句中定义的 with
子查询的结果);和 runs_scored
,显示每场比赛和每个球员他们得分的次数。 重要说明 - 还应该有一个 matches
表(包含比赛 ID 和其他详细信息,例如日期、主队、客队等);我很懒,只是从 runs_scored
表中提取了匹配 ID。
好的,所以数据设置看起来像这样:
创建或重新创建表(如果它们已经存在,请先删除它们)
drop table runs_scored purge;
drop table players purge;
drop table grades purge;
create table players (
player_id number primary key,name varchar2(20) not null,priority number not null unique
);
create table grades (
range varchar2(20) primary key,grade number not null unique
);
create table runs_scored (
match_id number not null -- references matches (should have that table too),player_id number references players,score number not null,primary key (match_id,player_id)
);
填充表格
insert into players
select 1001,'Sachin',1 from dual union all
select 1002,'Dhoni',2 from dual union all
select 1005,'Ravi',3 from dual
;
insert into grades
select '0-50',1 from dual union all
select '50-100',2 from dual union all
select '100-150',3 from dual
;
insert into runs_scored
select 1,1001,70 from dual union all
select 1,1002,45 from dual union all
select 3,1005,50 from dual union all
select 3,50 from dual union all
select 8,160 from dual union all
select 9,40 from dual union all
select 9,0 from dual union all
select 9,15 from dual
;
commit;
请注意,我假设必须为每场比赛单独分配成绩;如果不是这种情况,则可以轻松修改查询以应用所有比赛的总得分。
在输出中,我将排除未出现在 runs_scored
表中的玩家,以及可能出现得分为 0 的奇数玩家(并且,大概不应该包含在第一个)。当然,您可以有一个约束,要求分数首先 > 0。
那么这里有一种方法可以做到这一点,充分利用分析功能:
查询
with
g (runs,grade) as (
select to_number(substr(range,instr(range,'-') + 1)),grade from grades
union all
select null,1 + max(grade) from grades
),matches (match_id) as ( -- there should be a MATCHES table; simulated here
select distinct match_id from runs_scored
),prep (match_id,player_id,name,priority,score,cumul_score,grade) as (
select rs.match_id,rs.player_id,p.name,p.priority,rs.score,sum(rs.score) over (partition by rs.match_id order by p.priority),cast (null as number)
from runs_scored rs join players p on rs.player_id = p.player_id
union all
select m.match_id,null,g.runs,g.grade
from matches m cross join g
),comps (match_id,grade) as (
select match_id,last_value(player_id ignore nulls) over (partition by match_id
order by cumul_score desc,priority desc),last_value(name ignore nulls) over (partition by match_id
order by cumul_score desc,cumul_score - lead(cumul_score,1,0) over (partition by match_id
order by cumul_score desc,last_value(grade ignore nulls) over (partition by match_id
order by cumul_score desc,priority desc)
from prep p
)
select match_id,grade,score * grade as final
from comps
where name is not null and score > 0
order by match_id,cumul_score;
输出
MATCH_ID PLAYER_ID NAME SCORE GRADE FINAL
---------- ---------- ---------- ----- ----- ----------
1 1001 Sachin 50 1 50
1 1001 Sachin 20 2 40
1 1002 Dhoni 30 2 60
1 1002 Dhoni 15 3 45
3 1001 Sachin 50 1 50
3 1002 Dhoni 50 2 100
3 1005 Ravi 50 3 150
8 1002 Dhoni 50 1 50
8 1002 Dhoni 50 2 100
8 1002 Dhoni 50 3 150
8 1002 Dhoni 10 4 40
9 1001 Sachin 40 1 40
9 1002 Dhoni 10 1 10
9 1002 Dhoni 5 2 10
要了解它是如何工作的,在 with
子句中的每个连续子查询之后打破代码并从该子查询到 select *
可能会有所帮助。您将看到每个步骤的作用。