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.payout
和s.buyin
在一个聚合函数中。 我得到的错误:
列“sp.payout”必须出现在GROUP BY子句中或用于聚合函数中
我在Ubuntu Linux服务器上运行9.1。
我错过了什么,或者这可能是Postgres的一个真正的缺陷?
实际上,您并不使用聚合函数。 您正在使用窗口function 。 这就是PostgreSQL要求将sp.payout
和s.buyin
包含在GROUP BY
子句中的原因。
通过附加一个OVER
子句,聚合函数sum()
被转换成一个窗口函数,它在保留所有行的同时聚合每个分区的值。
您可以组合窗口函数和聚合函数 。 首先应用聚合。 我从你的描述不明白你想要处理多个支付/每次事件buyins。 作为一个猜测,我计算每个事件的总和。 现在,我可以从GROUP BY
子句中删除sp.payout
和s.buyin
,并为每个player
和event
获取一行:
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_id
和e.event_id
在它们各自的表中是PRIMARY KEY
。
我将e.event_id
添加到WINDOW
子句的ORDER BY
中,以达到确定性的sorting顺序。 (在同一天可能有多个事件。)还在结果中包含event_id
,以便每天区分多个事件。
虽然查询限制为单个播放器( WHERE p.player_id = 17
),但我们不需要将p.name
或p.player_id
添加到GROUP BY
和ORDER 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.date
和p.name
,在所有的子句中都有相同的sorting顺序,希望有一个性能上的好处。 否则,您可以删除那里的列。 (类似于第一个查询中的e.date
。)