Home > OS >  counting rows as new column then sum
counting rows as new column then sum

Time:07-14

I have a table with rows like this:

guildname rank name season
GuildOne 1 Player1 10
GuildOne 3 Player2 10
GuildOne 30 Player3 10
GuildTwo 7 Player4 10
GuildTwo 9 Player5 10
GuildTwo 31 Player6 10
GuildThree 63 Player7 10
GuildThree 393 Player8 10
GuildThree 99 Player9 10
GuildOne 216 Player10 10

I want to be able to get the points per guild based on their player's rank as follows:

  • rank >=1 && rank <= 150 : 4
  • rank >=151 && rank <= 300 : 2
  • rank >=301 && rank <= 600 : 1

and would like the resulting table to be:

guildname rank1to150 rank151to300 rank301up sumofpoints rank
GuildOne 3 1 0 14 1
GuildTwo 3 0 0 12 2
GuildThree 2 0 1 7 3

This is what I have so far:

WITH mpguildpoints (
  guildname,
  points,
  season
) AS (
    select mp_rankings.guildname,
    case when mp_rankings.rank >= 1 AND mp_rankings.rank <= 150 then 4
    when mp_rankings.rank >= 151 AND mp_rankings.rank <= 300 then 2
    when mp_rankings.rank >= 300 AND mp_rankings.rank <= 600 then 1
    end as points,
    season
    from mp_rankings
    group by mp_rankings.guildname,mp_rankings.rank,mp_rankings.season
    order by points desc, season desc
)

select guildname, count(points), sum(points) as totalpoints from mpguildpoints
where season = 10
group by guildname,points
order by totalpoints desc

CodePudding user response:

Use range operators, aggregation filters, and window functions:

with pointcalc as (
  select guildname, 
         case
           when int4range(1, 150, '[]')   @> rank then 4
           when int4range(151, 300, '[]') @> rank then 2
           when int4range(301, 600, '[]') @> rank then 1
           else 0
         end as points
    from rankings
), counts_sum as (
  select guildname, 
         count(1) filter (where points = 4) as rank1to150,
         count(1) filter (where points = 2) as rank151to300,
         count(1) filter (where points = 1) as rank301to600,
         sum(points) as sumofpoints
    from pointcalc
   group by guildname
)
select *, 
       rank() over (order by sumofpoints desc)
  from counts_sum
 order by rank;

db<>fiddle here

CodePudding user response:

Put each condition in a CASE expression and perform conditional aggregation:

with a (guildname, playerrank, name, season) as (
  select *
  from(values
('GuildOne', 1, 'Player1', 10),
('GuildOne', 3, 'Player2', 10),
('GuildOne', 30, 'Player3', 10),
('GuildTwo', 7, 'Player4', 10),
('GuildTwo', 9, 'Player5', 10),
('GuildTwo', 31, 'Player6', 10),
('GuildThree', 63, 'Player7', 10),
('GuildThree', 393, 'Player8', 10),
('GuildThree', 99, 'Player9', 10),
('GuildOne', 216, 'Player10', 10)
  ) as t
)
select
  b.*,
  dense_rank() over(order by score desc) as rank_
from (
select
  guildname,
  /*Count per group    */
  count(1) filter(where playerrank between 1 and 150) as rank1to150, 
  count(1) filter(where playerrank between 151 and 300) as rank151to300, 
  count(1) filter(where playerrank between 301 and 600) as rank300up, 
  sum(
    case
      /*Case stops on the first TRUE branch*/
      when playerrank <= 150 then 4
      when playerrank <= 300 then 2
      when playerrank <= 600 then 1
    end
  ) as score
  from a
group by 1
) as b
guildname  | rank1to150 | rank151to300 | rank300up | score | rank_
:--------- | ---------: | -----------: | --------: | ----: | ----:
GuildOne   |          3 |            1 |         0 |    14 |     1
GuildTwo   |          3 |            0 |         0 |    12 |     2
GuildThree |          2 |            0 |         1 |     9 |     3

db<>fiddle here

CodePudding user response:

You are almost near on your query, just need to do joins for the ranking.

select t1.guildname
    , t2.ct as rank1to150
    , t3.ct as rank151to300
    , t4.ct as rank301up
    , t1.totalpoints
    , t1.ct as "rank"
from 
    (select guildname, count(points) ct, sum(points) as totalpoints from mpguildpoints
    where season = 10
    group by guildname, points) t1
left join
    (select guildname, count(1) ct from mp_rankings
    where season = 10 and rank between 1 and 150
    group by guildname) t2 on t2.guildname = t1.guildname
left join
    (select guildname, count(1) ct from mp_rankings
    where season = 10 and rank between 151 and 300
    group by guildname) t3 on t3.guildname = t1.guildname   
left join
    (select guildname, count(1) ct from mp_rankings
    where season = 10 and rank > =301 
    group by guildname) t4 on t4.guildname = t1.guildname   
order by t1.rank asc
  • Related