16

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

这是我到目前为止的查询:

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 的真正缺陷?

4

1 回答 1

40

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

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

您可以组合窗口函数和聚合函数。首先应用聚合。从您的描述中,我不明白您希望如何处理每个事件的多个支出/买入。作为猜测,我计算每个事件的总和。现在我可以从子句中删除sp.payoutand并得到一行and :s.buyinGROUP BYplayerevent

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_ide.event_idPRIMARY KEY各自的表中。

我在子句中添加了确定的排序顺序e.event_id。(同一日期可能有多个事件。)还包括在结果中以区分每天的多个事件。ORDER BYWINDOWevent_id

虽然查询限制为单个玩家 ( WHERE p.player_id = 17),但我们不需要添加p.nameorp.player_idGROUP BYand ORDER BY。如果其中一个连接会过度地增加行,则结果总和将不正确(部分或完全相乘)。分组依据p.name当时无法修复查询。

我也e.dateGROUP BY子句中删除了。主键覆盖自 PostgreSQL 9.1 以来e.event_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.name定义为唯一 (?),否则按player_id另外的分组和排序以获得确定性排序顺序中的正确结果。

我只保留e.datep.nameGROUP BY所有子句中具有相同的排序顺序,希望能提高性能。否则,您可以删除那里的列。(与e.date第一个查询类似。)

于 2012-01-13T02:10:07.130 回答