用于计算rotisserie棒球积分的PostgreSQL查询

用于计算rotisserie棒球积分的PostgreSQL查询,sql,postgresql,ranking,window-functions,Sql,Postgresql,Ranking,Window Functions,我正试图编写一个PostgreSQL查询来计算虚拟棒球点数,如前所述。到目前为止,我所掌握的信息都可以在中找到,它正确地计算了每个统计数据的点数,但领带除外。平局团队的分数应按如下方式计算: 在打成平局的情况下,每一个参赛队都会得到一个平均的应付总分——即,在上面的例子中,如果两个队在一个类别中并列第一,每个队都会得到9.5分[(10+9)/2=9.5] 您可以在我的SQLfiddle中的和结果集中看到我的方法中的错误。在第一组结果中,与9支本垒打平手的球队各得3.5分(排名4和3的总和=7,除

我正试图编写一个PostgreSQL查询来计算虚拟棒球点数,如前所述。到目前为止,我所掌握的信息都可以在中找到,它正确地计算了每个统计数据的点数,但领带除外。平局团队的分数应按如下方式计算:

在打成平局的情况下,每一个参赛队都会得到一个平均的应付总分——即,在上面的例子中,如果两个队在一个类别中并列第一,每个队都会得到9.5分[(10+9)/2=9.5]

您可以在我的SQLfiddle中的和结果集中看到我的方法中的错误。在第一组结果中,与9支本垒打平手的球队各得3.5分(排名4和3的总和=7,除以2),而在第二组,与33 RBI平手的球队也各得3.5分(排名5、4、3和2的总和=14,除以4)


最简单的方法是什么来纠正这些错误,并将总分平均分配给排名靠前的团队?

蛮力法可能是计算未经调整的排名,如下所示:

select hr, sum(raw) / count(*)
  from (
      select hr,
             (select count(*) + 1 from stats) - row_number() over (order by hr desc) as raw
        from stats
    ) r
 group by hr
 order by hr desc

并将其与stats表连接起来,以获得给定分数的分数。

在我看来,如果示例不完整,最好在提供的字段中有一个候选键

  • 可以使用。这样子查询就不必对表中的所有行进行计数
  • 考虑以下查询的输出:

    SELECT 
        hr,rbi,
        rank() OVER h AS hr_rank,
        row_number() OVER h AS hr_rn,
        count(*) OVER () - rank() OVER h + 1 AS hr_aprx,
        rank() OVER r AS rbi_rank,
        row_number() OVER r AS rbi_rn,
        count(*) OVER () - rank() OVER r + 1 AS rbi_aprx,
        count(*) OVER () AS cnt
    FROM 
        stats
    WINDOW h AS (ORDER BY hr DESC), r AS (ORDER BY rbi DESC);
    
    此查询提供的信息与前两个查询相同。如果您查看该表的
    EXPLAIN(analyze,buffers)
    输出,您将看到该表只被访问一次

    我在这里将点列命名为
    %\u aprx
    ,因为这些是近似点,我们必须计算平均值

  • 现在,由于我们已经准备了一些数据用于进一步的计算,我们将不得不使用一个子查询。这是因为我们必须使用
    %\u aprx
    列进行数据分组。我将在这里使用,因为我发现命名子查询看起来更好
  • 考虑这个查询():

    我正在将
    avg()
    调用的结果类型转换为
    float
    ,以除去一系列的零。不过,你可以选择在这里代替

    我还添加了两个订购条件,因为仅凭
    ttl_pts
    订购是不够的


    请注意,在外部查询的窗口定义中,
    orderby
    故意漏掉。有了它,您将获得运行平均效果(您可以更改查询并查看您自己)。

    这是一个好的开始,但对于十个统计数据中的每一个,重复该子查询(使用联接)将变得相当笨拙。希望有一种更简单的方法。笨拙在SQL中并不少见。也许它可以以某种方式转换成一个函数,其中stats列是一个参数。Postgres足够聪明,可以避免对内部子查询进行重复扫描。您可以通过查看
    EXPLAIN
    输出来确认这一点。您可以解释模式中的
    hr
    rbi
    是什么吗?此外,如果你瞄准和浮动分数(如
    9.5
    ),为什么要使用
    integer
    而不是
    numeric
    类型?HR和RBI是我想要排名的值——在这种情况下,对于棒球队也是如此。这些都是整数,但是在平局的情况下,球队的排名应该是平均的,就像我问题中的雅虎链接一样。太棒了!是的,我的例子遗漏了一些细节,但我是故意这样做的。我将其简化为一个“最小工作示例”,因为我运行此查询的“表”实际上是另一个复杂嵌套查询的结果,该查询汇总了各个游戏的团队结果,我不想让问题看起来比实际情况更复杂。使用您的解决方案,我可以定期使用聚合结果构建一个临时表,或者调整它以运行聚合查询。谢谢
    WITH ranks AS (
        SELECT 
            hr, rbi,
            rank() OVER h AS hr_rank,
            row_number() OVER h AS hr_rn,
            count(*) OVER () - rank() OVER h + 1 AS hr_aprx,
            rank() OVER r AS rbi_rank,
            row_number() OVER r AS rbi_rn,
            count(*) OVER () - rank() OVER r + 1 AS rbi_aprx,
            count(*) OVER () AS cnt
        FROM 
            stats
        WINDOW h AS (ORDER BY hr DESC), r AS (ORDER BY rbi DESC)
    )
    SELECT 
        hr, rbi,
        (avg(hr_rn) OVER h)::float AS hr_pts,
        (avg(rbi_rn) OVER r)::float AS rbi_pts,
        (avg(hr_rn) OVER h + avg(rbi_rn) OVER r)::float AS ttl_pts
    FROM 
        ranks
    WINDOW h AS (PARTITION BY hr_aprx), r AS (PARTITION BY rbi_aprx)
    ORDER BY 
        ttl_pts DESC, hr_pts DESC;