使用其他(不同的)过滤器聚合列
Posted
技术标签:
【中文标题】使用其他(不同的)过滤器聚合列【英文标题】: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,还没有,但感谢您提到聚合过滤器,我以后一定会考虑升级。 【参考方案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;
The manual
Postgres Wiki
Depesz blog post
在 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;
详情:
For absolute performance, is SUM faster or COUNT?【讨论】:
以上是关于使用其他(不同的)过滤器聚合列的主要内容,如果未能解决你的问题,请参考以下文章