计算两个总数的运行比率

Posted

技术标签:

【中文标题】计算两个总数的运行比率【英文标题】:Compute a running ratio of two totals 【发布时间】:2015-03-13 18:05:06 【问题描述】:

我有一个 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 的每一行。请注意,上面选择的命中是 at-蝙蝠):

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

【问题讨论】:

【参考方案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;

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

Conditional SQL count

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

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

Select last n rows without use of order by clause

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

转换为numeric 是为了避免整数除法,这会截断小数位并导致有用性问题。将结果四舍五入为两位小数。

【讨论】:

谢谢;有没有办法通过主键显式排序?或者这和依赖当前的默认排序一样糟糕? @MichaelCurry:当然,只需将 PK 列的名称放在那里。好/坏是您要求的函数。没有ORDER BY 的“默认”顺序只是一个可以随时更改的任意顺序。这是 Postgres 以最方便的方式呈现行的方式,通常是磁盘上行的当前物理顺序,这通常与插入行的顺序一致。但这可以随时改变,VACUUMCLUSTER 或任何 UPDATEDELETE 等。【参考方案2】:

使用条件聚合。您还没有指定 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';

?? 放入适当的排序列。

【讨论】:

在这种情况下,数据库中的行恰好已经按正确的顺序排列。所以 OVER 子句就是OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)。这给出了正确的结果,但我不确定是否有更好的方法来做到这一点。 没有“按正确顺序”排列的表格。表格代表无序集。

以上是关于计算两个总数的运行比率的主要内容,如果未能解决你的问题,请参考以下文章

如何从两个表中获取总数并使用该值进行计算? (sql, asp)

如何从相同的数据构造2列并计算比率?

在某些条件下,找到两个数组之间(逐项)比率的最大值或最小值

sql:选择由另一列分组的两列值的计数并获得两个计数的比率

chrony配置

比率(ratio)