Sql Postgres窗口函数和分组异常
我正在尝试组合一个查询,该查询将检索用户在一段时间内的统计数据(损益),作为累积结果 以下是我到目前为止的疑问:Sql Postgres窗口函数和分组异常,sql,postgresql,aggregate-functions,window-functions,Sql,Postgresql,Aggregate Functions,Window Functions,我正在尝试组合一个查询,该查询将检索用户在一段时间内的统计数据(损益),作为累积结果 以下是我到目前为止的疑问: 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 e
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
查询将运行。然而,结果有点不正确。原因是一个事件
可以有多个游戏(不同的sp.payouts
)。因此,如果一个用户在一个具有不同支付的事件中有两个结果(即,每个事件有4个游戏,一个用户从一个事件中获得20英镑,从另一个事件中获得40英镑),则上述结果将显示为多行
显而易见的解决方案是将分组修改为:
GROUP BY p.name, e.date, e.event_id
然而,Postgres对此表示不满,因为它似乎没有意识到sp.payout
和s.buyin
在聚合函数中。我得到一个错误:
列“sp.payout”必须出现在GROUP BY子句中或用于
聚合函数
我正在Ubuntu Linux服务器上运行9.1。
我是否遗漏了什么,或者这可能是Postgres中的真正缺陷?事实上,您使用的是聚合函数,而不是。您正在使用。这就是为什么PostgreSQL要求将sp.payout
和s.buyin
包含在groupby
子句中
通过附加一个OVER
子句,聚合函数sum()
被转换成一个窗口函数,它在保留所有行的同时聚合每个分区的值
您可以组合窗口函数和聚合函数。首先应用聚合。从您的描述中,我不明白您希望如何处理每次活动的多个付款/购买。作为猜测,我计算了每个事件的它们的总和。现在,我可以从GROUP BY
子句中删除sp.payout
和s.buyin
,并为每个玩家
和事件
获取一行:
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;
在这个表达式中:sum(sum(sp.payout))OVER w
,外部的sum()
是一个窗口函数,内部的sum()
是一个聚合函数
假设p.player\u id
和e.event\u id
在各自的表中是主键
我将e.event\u id
添加到WINDOW
子句的ORDER BY
,以获得确定的排序顺序。(同一天可能有多个事件。)结果中还包括event_id
,以区分每天的多个事件
虽然查询仅限于单个玩家(其中p.player_id=17
),但我们不需要将p.name
或p.player_id
添加到分组依据
和订单依据
。如果其中一个联接将不适当地相乘行,则得到的总和将不正确(部分相乘或完全相乘)。按p.name进行分组当时无法修复查询
我还从groupby
子句中删除了e.date
。主键e.event\u id
覆盖输入行的所有列
如果将查询更改为一次返回多个玩家,请调整:
...
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.player_id<17——示例——多个玩家
按p.name、p.player\u id、e.date、e.event\u id——e.date和p.name进行分组
窗口w AS(按p.name、p.player\u id、e.date、e.event\u id排序)
按p.name、p.player\u id、e.date、e.event\u id排序;
除非p.name
被定义为唯一(?),否则按player_id
进行分组和排序,以获得确定排序顺序的正确结果
我只将e.date
和p.name
保留在groupby
中,以便在所有子句中具有相同的排序顺序,希望性能有所提高。否则,您可以删除那里的列。(与第一个查询中的e.date
类似。)第一个查询正在运行,但是查询的输出没有给出所需的结果。我可以看出什么样的修正案在理论上是可行的,但博士后不喜欢它。我稍后会尝试上面的方法,并让您知道。但是,如果一个“event_id”有多个“payout”金额,那么您的查询输出中似乎会有两行。我刚刚尝试了您建议的修改,它确实返回了多行,其中一个事件id有多个sp.payout值。@马丁:见我的修正答案。您不需要在窗口函数中按p.name、e.event\u id进行分区,分组才能按预期工作吗?@RyanTuck:有几个未解释/模糊的位。我改进和澄清了(或者至少希望如此)。