Aggregate columns with additional (distinct) filters

后端 未结 3 880
一个人的身影
一个人的身影 2020-11-22 10:32

This code works as expected, but I it\'s long and creepy.

select p.name, p.played, w.won, l.lost from

(select users.name, count(games.name) as played
from u         


        
相关标签:
3条回答
  • 2020-11-22 11:13

    The aggregate FILTER clause in Postgres 9.4 or newer is shorter and faster:

    SELECT u.name
         , count(*) FILTER (WHERE g.winner_id  > 0)    AS played
         , count(*) FILTER (WHERE g.winner_id  = u.id) AS won
         , count(*) FILTER (WHERE g.winner_id <> u.id) AS lost
    FROM   games g
    JOIN   users u ON u.id IN (g.player_1_id, g.player_2_id)
    GROUP  BY u.name;
    
    • The manual
    • Postgres Wiki
    • Depesz blog post

    In Postgres 9.3 (or any version) this is still shorter and faster than nested sub-selects or CASE expressions:

    SELECT u.name
         , count(g.winner_id  > 0 OR NULL)    AS played
         , count(g.winner_id  = u.id OR NULL) AS won
         , count(g.winner_id <> u.id OR NULL) AS lost
    FROM   games g
    JOIN   users u ON u.id IN (g.player_1_id, g.player_2_id)
    GROUP  BY u.name;
    

    Details:

    • For absolute performance, is SUM faster or COUNT?
    0 讨论(0)
  • 2020-11-22 11:24
    select users.name, 
           count(case when games.winner_id > 0 
                      then games.name 
                      else null end) as played,
           count(case when games.winner_id = users.id 
                      then games.name 
                      else null end) as won,
           count(case when games.winner_id != users.id 
                      then games.name 
                      else null end) as lost
    from users inner join games 
         on games.player_1_id = users.id or games.player_2_id = users.id
    group by users.name;
    
    0 讨论(0)
  • 2020-11-22 11:31

    This is a case where correlated subqueries may simplify the logic:

    select u.*, (played - won) as lost
    from (select u.*,
                 (select count(*)
                  from games g
                  where g.player_1_id = u.id or g.player_2_id = u.id
                 ) as played,
                 (select count(*)
                  from games g
                  where g.winner_id = u.id
                 ) as won
          from users u
         ) u;
    

    This assumes that there are no ties.

    0 讨论(0)
提交回复
热议问题