【问题标题】:Aggregate columns with additional (distinct) filters使用其他(不同的)过滤器聚合列
【发布时间】:2015-01-24 00:28:08
【问题描述】:

此代码按预期工作,但我很长而且令人毛骨悚然。

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

(select users.name, count(games.name) as played
from users
inner join games on games.player_1_id = users.id
where games.winner_id > 0
group by users.name
union
select users.name, count(games.name) as played
from users
inner join games on games.player_2_id = users.id
where games.winner_id > 0
group by users.name) as p

inner join

(select users.name, count(games.name) as won
from users
inner join games on games.player_1_id = users.id
where games.winner_id = users.id
group by users.name
union
select users.name, count(games.name) as won
from users
inner join games on games.player_2_id = users.id
where games.winner_id = users.id
group by users.name) as w on p.name = w.name

inner join

(select users.name, count(games.name) as lost
from users
inner join games on games.player_1_id = users.id
where games.winner_id != users.id
group by users.name
union
select users.name, count(games.name) as lost
from users
inner join games on games.player_2_id = users.id
where games.winner_id != users.id
group by users.name) as l on l.name = p.name

如您所见,它由 3 个用于检索的重复部分组成:

  • 玩家姓名和他们玩的游戏数量
  • 玩家姓名和他们赢得的游戏数量
  • 玩家姓名和他们输掉的游戏数量

每一个也由两部分组成:

  • 玩家姓名和他们作为玩家_1参与的游戏数量
  • 玩家姓名和他们作为玩家_2参与的游戏数量

如何简化?

结果如下:

           name            | played | won | lost 
---------------------------+--------+-----+------
 player_a                  |      5 |   2 |    3
 player_b                  |      3 |   2 |    1
 player_c                  |      2 |   1 |    1

【问题讨论】:

  • 你还没有运行 postgres 9.4 是吗?
  • @JoeLove,还没有,但感谢您提到聚合过滤器,我以后一定会考虑升级。

标签: sql postgresql aggregate-functions aggregate-filter


【解决方案1】:

这是一种关联子查询可以简化逻辑的情况:

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;

这假设没有关系。

【讨论】:

    【解决方案2】:
    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;
    

    【讨论】:

    • 这在 9.4 之后实施聚合过滤器时将完全不重要。这些类型的“案例”陈述将成为过去。
    【解决方案3】:

    Postgres 9.4 或更新版本中的 aggregate FILTER 子句 更短更快:

    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;
    

    在 Postgres 9.3(或 any 版本)中,这仍然比嵌套子选择或 CASE 表达式更短且更快:

    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;
    

    详情:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-27
      • 1970-01-01
      • 2020-03-27
      • 2016-02-25
      • 2020-06-22
      相关资源
      最近更新 更多