Postgres窗口function和组例外

我试图把一个查询,将检索一个用户的统计数据(利润/损失)作为累积的结果,在一段时间。

以下是我到目前为止的查询:

SELECT p.name, e.date, sum(sp.payout) OVER (ORDER BY e.date) - sum(s.buyin) OVER (ORDER BY e.date) AS "Profit/Loss" FROM result r JOIN game g ON r.game_id = g.game_id JOIN event e ON g.event_id = e.event_id JOIN structure s ON g.structure_id = s.structure_id JOIN structure_payout sp ON g.structure_id = sp.structure_id AND r.position = sp.position JOIN player p ON r.player_id = p.player_id WHERE p.player_id = 17 GROUP BY p.name, e.date, e.event_id, sp.payout, s.buyin ORDER BY p.name, e.date ASC 

查询将会运行。 但是,结果稍微不正确。 原因是一个event可以有多个游戏(不同的sp.payouts )。 因此,如果用户在支付不同的事件中具有2个结果(即,每个事件有4个游戏,并且用户从一个获得20英镑,而另一个从另一个获得了40英镑),则上面出现多行。

显而易见的解决scheme是将GROUP BY修改为:

 GROUP BY p.name, e.date, e.event_id 

但是,Postgres抱怨,因为它似乎并没有认识到sp.payouts.buyin在一个聚合函数中。 我得到的错误:

列“sp.payout”必须出现在GROUP BY子句中或用于聚合函数中

我在Ubuntu Linux服务器上运行9.1。
我错过了什么,或者这可能是Postgres的一个真正的缺陷?

实际上,您并不使用聚合函数。 您正在使用窗口function 。 这就是PostgreSQL要求将sp.payouts.buyin包含在GROUP BY子句中的原因。

通过附加一个OVER子句,聚合函数sum()被转换成一个窗口函数,它在保留所有行的同时聚合每个分区的值。

您可以组合窗口函数和聚合函数 。 首先应用聚合。 我从你的描述不明白你想要处理多个支付/每次事件buyins。 作为一个猜测,我计算每个事件的总和。 现在,我可以从GROUP BY子句中删除sp.payouts.buyin ,并为每个playerevent获取一行:

 SELECT p.name , e.event_id , e.date , sum(sum(sp.payout)) OVER w - sum(sum(s.buyin )) OVER w AS "Profit/Loss" FROM player p JOIN result r ON r.player_id = p.player_id JOIN game g ON g.game_id = r.game_id JOIN event e ON e.event_id = g.event_id JOIN structure s ON s.structure_id = g.structure_id JOIN structure_payout sp ON sp.structure_id = g.structure_id AND sp.position = r.position WHERE p.player_id = 17 GROUP BY e.event_id WINDOW w AS (ORDER BY e.date, e.event_id) ORDER BY e.date, e.event_id; 

在这个expression式中: sum(sum(sp.payout)) OVER w ,outer sum()是一个窗函数,inner sum()是一个集合函数。

假设p.player_ide.event_id在它们各自的表中是PRIMARY KEY

我将e.event_id添加到WINDOW子句的ORDER BY中,以达到确定性的sorting顺序。 (在同一天可能有多个事件。)还在结果中包含event_id ,以便每天区分多个事件。

虽然查询限制为单个播放器( WHERE p.player_id = 17 ),但我们不需要将p.namep.player_id添加到GROUP BYORDER BY 。 如果其中一个连接会不必要地增加行数,结果总和就会不正确(部分或完全相乘)。 然后按p.name分组不能修复查询。

我也从GROUP BY子句中删除了e.date 。 主键e.event_id涵盖自PostgreSQL 9.1以来的所有input行的列。

如果您更改查询以一次返回多个玩家,请修改:

 ... WHERE p.player_id < 17 -- example - multiple players GROUP BY p.name, p.player_id, e.date, e.event_id -- e.date and p.name redundant WINDOW w AS (ORDER BY p.name, p.player_id, e.date, e.event_id) ORDER BY p.name, p.player_id, e.date, e.event_id; 

除非p.name被定义为唯一(?),否则按player_id分组和sorting以获得确定性sorting顺序的正确结果。

我只保留GROUP BY e.datep.name ,在所有的子句中都有相同的sorting顺序,希望有一个性能上的好处。 否则,您可以删除那里的列。 (类似于第一个查询中的e.date 。)