计算两个总计的运行比率

时间:2015-03-13 18:05:06

标签: sql postgresql window-functions aggregate-filter

我有一个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的所有先前行的计数,以及描述命中的所有前面行的计数。除以这些值可得出该行的击球率。

2 个答案:

答案 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的强制转换是为了避免整数除法,它会切断小数位并导致可疑的有用性。将结果四舍五入为两位小数。