计算两个总数的 运行 比率

Compute a running ratio of two totals

我有一个 PostgreSQL 9.4.1 数据库(Retrosheet 数据),其中 table events 每个棒球比赛包含一行。我想计算给定球员的 运行 安打率:公式是(到目前为止的总命中数)/(到目前为止的有效击球总数)。

我可以使用 window 函数为 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 的每一行。请注意命中上面选择的是 at-bats 的一个子集):

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' 比赛的每一行,我将计算两个函数:描述击球的所有前面行的计数,以及描述命中的所有前面行的计数。将这些相除得到该行的 运行 安打率。

使用条件聚合。您没有指定 order by 子句,这是 window 函数真正需要的。您想要的查询类似于:

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';

??.

输入适当的排序栏

假设(因为尚未声明)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;

由于您使用的是 pg 9.4,因此可以使用新的聚合 FILTER 子句。相关回答:

框架定义ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW是默认的,所以你不必声明它。

但是数据库中没有“自然顺序”table。不要将其与电子表格混淆。您需要ORDER BY 定义它。我正在使用虚列 input_ts,将其替换为定义排序顺序的列(列表)。更多:

  • Select last n rows without use of order by clause

我避免使用 NOT IN,因为它表现出带有 NULL 值的棘手行为。

强制转换为 numeric 是为了避免整数除法,因为整数除法会切断小数位并导致有用性受到质疑。将结果四舍五入到两位小数。