我有一个PostgreSQL 9.4.1数据库(Retrosheet数据),表格events
包含每个棒球比赛一行。我想计算给定玩家的正在运行的击球平均值:公式是(到目前为止的命中总数)/(到目前为止有效击球总数)。
我可以使用窗口函数来获取David Ortiz的总点击次数,其播放器代码为ortid001
,使用以下查询:
SELECT count(*) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM events WHERE bat_id='ortid001' AND (event_cd='20' OR event_cd='21'
OR event_cd='22' OR event_cd='23');
(涉及event_cd
的条款只标识哪些行被视为命中。)
使用相同的技术,我可以得到一个总计的at-bats(event_cd
子句拒绝所有不计入at-bat的行。请注意,上面选择的命中是一个子集在 - 蝙蝠):
SELECT count(*) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM events WHERE bat_id='ortid001' AND (event_cd != '11' AND
event_cd!='14' AND event_cd!='15' AND event_cd!='16' AND
event_cd!='17');
我如何结合这些?理想情况下,对于描述具有bat_id='some_player_id'
的游戏的每一行,我将计算两个函数:描述at-bat的所有先前行的计数,以及描述命中的所有前面行的计数。除以这些值可得出该行的击球率。
答案 0 :(得分:1)
使用条件聚合。您尚未指定order by
子句,这是您真正需要的窗口函数。您想要的查询类似于:
SELECT sum(case when event_cd in ('20', '21', '22', '23') then 1 else 0 end) OVER (ORDER BY ??),
sum(case when event_cd not in ('11', '14', '15', '16', '17') then 1 else 0 end) OVER (ORDER BY ??),
(sum(case when event_cd in ('20', '21', '22', '23') then 1.0 else 0 end) OVER (ORDER BY ??) /
sum(case when event_cd not in ('11', '14', '15', '16', '17') then 1 else 0 end) OVER (ORDER BY ??)
) as ratio
FROM events
WHERE bat_id = 'ortid001';
将适当的排序列放入??
。
答案 1 :(得分:1)
假设(因为它尚未声明)event_cd
是数据类型integer
并且可以为NULL。
SELECT *, round(hit::numeric / at_bat, 2) AS rate
FROM (
SELECT input_ts
, count(*) FILTER (WHERE event_cd = ANY ('{20,21,22,23}'::int[]))
OVER (ORDER BY input_ts) AS hit
, count(*) FILTER (WHERE NOT (event_cd = ANY ('{11,14,15,16,17}'::int[])))
OVER (ORDER BY input_ts) AS at_bat
FROM events
WHERE bat_id = 'ortid001'
) sub
ORDER BY input_ts;
由于您使用的是第9.4页,因此可以使用新的聚合FILTER
子句。相关回答:
帧定义ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
是默认值,因此您无需声明它。
但是数据库表中没有“自然顺序”。不要将其与电子表格混淆。您需要使用ORDER BY
进行定义。我正在使用虚列input_ts
,将其替换为定义排序顺序的(列表)列。更多:
我避免使用NOT IN
,因为它使用NULL值表现出棘手的行为。
对numeric
的强制转换是为了避免整数除法,它会切断小数位并导致可疑的有用性。将结果四舍五入为两位小数。