我正在尝试整理一个查询,该查询将在一段时间内检索用户的统计信息(盈利/亏损)作为累积结果。
这是我到目前为止的查询:
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英镑),则上面出现多行。
显而易见的解决方案是将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的真正缺陷吗?
答案 0 :(得分:31)
事实上,您使用聚合函数不。您正在使用 window functions 。这就是PostgreSQL要求sp.payout
和s.buyin
包含在GROUP BY
子句中的原因。
通过附加OVER
子句,聚合函数sum()
将转换为窗口函数,该函数在保持所有行时聚合每个分区的值。
您可以合并窗口函数和聚合函数。首先应用聚合。我从你的描述中不理解你想如何处理每个事件的多个支付/购买。作为猜测,我计算每个事件的总和。 现在我可以从sp.payout
子句中删除s.buyin
和GROUP BY
,并按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;
在此表达式中:sum(sum(sp.payout)) OVER w
,外部sum()
是窗口函数,内部sum()
是聚合函数。
假设p.player_id
和e.event_id
在各自的表格中为PRIMARY KEY
。
我将e.event_id
添加到ORDER BY
子句的WINDOW
以获得确定的排序顺序。 (同一日期可能会有多个事件。)结果中还包括event_id
,以区分每天的多个事件。
虽然查询限制为单个播放器(WHERE p.player_id = 17
),但我们无需将p.name
或p.player_id
添加到GROUP BY
和ORDER BY
。如果其中一个连接会过度地乘以行,则得到的总和将是不正确的(部分或完全相乘)。按p.name
分组无法修复查询。
我还从e.date
子句中删除了GROUP BY
。主键e.event_id
涵盖输入行since PostgreSQL 9.1的所有列。
如果 您将查询更改为一次返回多个玩家,请调整:
...
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
分组和排序还会以确定的排序顺序获得正确的结果。
我只在e.date
中保留p.name
和GROUP BY
,以便在所有条款中具有相同的排序顺序,希望获得性能优势。否则,您可以删除那里的列。 (与第一个查询中的e.date
类似。)