Mar*_*tin 12 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 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
Run Code Online (Sandbox Code Playgroud)
查询将运行.但是,结果略有不正确.原因是一个人event可以有多个游戏(有不同的sp.payouts).因此,如果用户在具有不同支出的事件中具有2个结果(即,每个事件有4个游戏,并且用户从一个获得20英镑,而从另一个获得40英镑),则上面出现多行.
显而易见的解决方案是修改GROUP BY为:
GROUP BY p.name, e.date, e.event_id
Run Code Online (Sandbox Code Playgroud)
然而,Postgres在此抱怨,因为它似乎没有认识到sp.payout并且s.buyin在一个聚合函数内.我收到错误:
列"sp.payout"必须出现在GROUP BY子句中或用于聚合函数
我在Ubuntu Linux服务器上运行9.1.
我错过了什么,或者这可能是Postgres的真正缺陷吗?
Erw*_*ter 32
事实上,您并非使用聚合函数.您正在使用窗口功能.这就是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;
Run Code Online (Sandbox Code Playgroud)
在这个表达式中:sum(sum(sp.payout)) OVER w,outer sum()是一个window函数,inner sum()是一个聚合函数.
假设p.player_id和e.event_id是PRIMARY KEY它们各自的表英寸
我加入e.event_id到ORDER BY了的WINDOW条款以确定性的排序顺序到达.(同一日期可能有多个事件.)event_id结果中还包括每天区分多个事件.
虽然查询限制为单个 player(WHERE p.player_id = 17),但我们不需要添加p.name或p.player_idto GROUP BY和ORDER BY.如果其中一个连接会过度地乘以行,则得到的总和将是不正确的(部分或完全相乘).然后分组p.name无法修复查询.
我也e.date从GROUP 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;
Run Code Online (Sandbox Code Playgroud)
除非p.name定义为唯一(?),否则通过分组和顺序以player_id确定的排序顺序获得正确的结果.
我只保留e.date并且p.name在GROUP BY所有条款中具有相同的排序顺序,希望获得性能优势.否则,您可以删除那里的列.(仅e.date在第一个查询中类似.)