用于计算rotisserie棒球点的PostgreSQL查询

时间:2014-05-17 18:52:50

标签: sql postgresql ranking window-functions

我正在尝试编写一个PostgreSQL查询来计算幻想棒球点,如here所述。我到目前为止所提供的内容在this SQLfiddle中可用,它可以正确计算每个统计数据的点数,除非是关系。被绑定的团队的积分应该像这样计算:

  

在平局的情况下,所涉及的每支球队都会得到应付总分数的平均值 - 也就是说,在上面的例子中,如果两个队伍在一个类别中并列第一,那么每个队伍将获得9.5分[(10 + 9) )/ 2 = 9.5]。

您可以在我的SQLfiddle的firstsecond结果集中查看我的方法中的错误。在第一个结果集中,与9个本垒打并列的球队应该得到3.5分(等级4和3 = 7 = 2除以2),而在第二组中,与33打点相关的团队也应该得到3.5分。 (等级为5,4,3和2 = = 14除以4)。

纠正这些错误的最简单方法是什么,并在每个统计数据的排名中平均分配总分数?

2 个答案:

答案 0 :(得分:1)

蛮力方法可能是计算未调整的等级,如下所示:

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表结合以获得给定分数的分数。

答案 1 :(得分:1)

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

  1. 可以使用any aggregate function as window one。这消除了子查询计算表中所有行的必要性。
  2. 考虑以下查询的输出:

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

    此查询提供与前2个查询相同的信息。如果你要查看它的EXPLAIN (analyze, buffers)输出,你会看到该表只被访问一次。

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

    1. 现在,由于我们准备了一些数据用于进一步计算,我们将不得不使用子查询。这是因为我们必须使用%_aprx列进行数据分组。我将在这里使用CTE,因为我发现命名的子查询看起来更好。
    2. 考虑此查询(also on sql-fiddle):

      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;
      

      我正在将结果类型的avg()调用转换为float以删除一系列零。你可以在这里选择use round() function

      我还添加了2个排序条件,仅按ttl_pts排序是不够的。

      请注意,外部查询的窗口定义ORDER BY有意错过。有了它,你将获得一个平均运行效果(你可以改变查询并看到自己)。