使用前2个条目中的值将移动平均列添加到表中

时间:2019-10-20 11:02:08

标签: mysql sql database

我当前在数据库中具有以下简化表。积分表包含针对每位用户投票的每种投标形式授予的积分行。

我想在此表中添加一列,该列的每一行显示授予该用户的先前两分的平均值。

Users
+----+----------------------+
| id | name                 |
+----+----------------------+
|  1 | Flossie Schamberger  |
|  2 | Lawson Graham        |
|  3 | Hadley Reilly        |
+----+----------------------+

Bid Forms
+----+-----------------+
| id | name            |
+----+-----------------+
|  1 | Summer 2017     |
|  2 | Winter 2017     |
|  3 | Summer 2018     |
|  4 | Winter 2019     |
|  5 | Summer 2019     |
+----+-----------------+

Points
+-----+---------+--------------------+------------+------------+
| id  | user_id | leave_bid_forms_id | bid_points | date       |
+-----+---------+--------------------+------------+------------+
|   1 |       1 |                  1 |          6 | 2016-06-19 |
|   2 |       2 |                  1 |          8 | 2016-06-19 |
|   3 |       3 |                  1 |         10 | 2016-06-19 |
|   4 |       1 |                  2 |          4 | 2016-12-18 |
|   5 |       2 |                  2 |          8 | 2016-12-18 |
|   6 |       3 |                  2 |          4 | 2016-12-18 |
|   7 |       1 |                  3 |         10 | 2017-06-18 |
|   8 |       2 |                  3 |         12 | 2017-06-18 |
|   9 |       3 |                  3 |          4 | 2017-06-18 |
|  10 |       1 |                  4 |          4 | 2017-12-17 |
|  11 |       2 |                  4 |          4 | 2017-12-17 |
|  12 |       3 |                  4 |          2 | 2017-12-17 |
|  13 |       1 |                  5 |         16 | 2018-06-17 |
|  14 |       2 |                  5 |         12 | 2018-06-17 |
|  15 |       3 |                  5 |         10 | 2018-06-17 |
+-----+---------+--------------------+------------+------------+

对于点表中的每一行,我希望按如下方式计算average_points列。

“平均分”列是该用户之前2个点的平均值。因此,对于该表中每个用户的第一个条目,平均值显然为0,因为之前没有授予任何积分。

应使用日期列确定每个用户的前2点。

下表是我希望作为最终输出的内容。 为了清楚起见,在表的一边,我添加了用于得出averaged_points列中值的计算和数字。

+-----+---------+--------------------+------------+-----------------+
| id  | user_id | leave_bid_forms_id | date       | averaged_points |
+-----+---------+--------------------+------------+-----------------+
|   1 |       1 |                  1 | 2016-06-19 |               0 |    ( 0 + 0 ) / 2 
|   2 |       2 |                  1 | 2016-06-19 |               0 |    ( 0 + 0 ) / 2 
|   3 |       3 |                  1 | 2016-06-19 |               0 |    ( 0 + 0 ) / 2 
|   4 |       1 |                  2 | 2016-12-18 |               3 |    ( 6 + 0 ) / 2 
|   5 |       2 |                  2 | 2016-12-18 |               4 |    ( 8 + 0 ) / 2 
|   6 |       3 |                  2 | 2016-12-18 |               5 |    ( 10 + 0) / 2 
|   7 |       1 |                  3 | 2017-06-18 |               5 |    ( 4 + 6 ) / 2 
|   8 |       2 |                  3 | 2017-06-18 |               8 |    ( 8 + 8 ) / 2 
|   9 |       3 |                  3 | 2017-06-18 |               7 |    ( 4 + 10) / 2 
|  10 |       1 |                  4 | 2017-12-17 |               7 |    ( 10 + 4) / 2 
|  11 |       2 |                  4 | 2017-12-17 |              10 |    ( 12 + 8) / 2 
|  12 |       3 |                  4 | 2017-12-17 |               4 |    ( 4 + 4 ) / 2 
|  13 |       1 |                  5 | 2018-06-17 |               7 |    ( 4 + 10) / 2 
|  14 |       2 |                  5 | 2018-06-17 |               8 |    ( 4 + 12) / 2  
|  15 |       3 |                  5 | 2018-06-17 |               3 |    ( 2 + 4 ) / 2 
+-----+---------+--------------------+------------+-----------------+

我一直在尝试使用子查询来解决此问题,因为AVG似乎不受我拥有的任何LIMIT子句的影响。

到目前为止,我已经想到了

select id, user_id, leave_bid_forms_id, `date`, 
(
    SELECT
          AVG(bid_points) 
          FROM (
            Select `bid_points`
            FROM points as p2
            ORDER BY p2.date DESC
            Limit 2
                ) as thing
      ) AS average_points
from points as p1

这是in this sqlfiddle,但老实说,我在这里没什么意思。

我在正确的道路上吗?想知道是否有人可以告诉我我需要调整的地方!

谢谢。

编辑

使用以下答案作为基础,我能够调整sql以使其与原始sqlfiddle中提供的表一起使用。

我已将其添加到this sqlfiddle to show it working

与上面的代码匹配的更正的sql是

select p.*,
       IFNULL(( (coalesce(points_1, 0) + coalesce(points_2, 0)) /
         ( (points_1 is not null) + (points_2 is not null) )
       ),0) as prev_2_avg
from (select p.*,
             (select p2.bid_points
              from points p2
              where p2.user_id = p.user_id and
                    p2.date < p.date
              order by p2.date desc
              limit 1
             ) as points_1,

             (select p2.bid_points
              from points p2
              where p2.user_id = p.user_id and
                    p2.date < p.date
              order by p2.date desc
              limit 1, 1
             ) as points_2

      from points as p
     ) p;

尽管我将要问另一个问题,以最佳的方式使这种动态变化与需要平均的以前的点数保持一致。

1 个答案:

答案 0 :(得分:1)

您可以使用MySQL 8中引入的window functions

select p.*,
       avg(points) over (partition by user_id
                         order by date
                         rows between 2 preceding and 1 preceding
                        ) as prev_2_avg
from p;

在早期版本中,这是一个真正的痛苦,因为MySQL不支持嵌套的关联子句。一种方法是每个方法都有一个单独的列:

select p.*,
       ( (coalesce(points_1, 0) + coalesce(points_2, 0)) /
         ( (points_1 is not null) + (points_2 is not null) )
       ) as prev_2_avg
from (select p.*,
             (select p2.points
              from points p2
              where p2.user_id = p.user_id and
                    p2.date < p.date
              order by p2.date desc
              limit 1
             ) as points_1,
             (select p2.points
              from points p2
              where p2.user_id = p.user_id and
                    p2.date < p.date
              order by p2.date desc
              limit 1, 1
             ) as points_2
      from p
     ) p;
相关问题