【问题标题】:Create a ROLLING sum over a period of time in mysql在mysql中创建一段时间内的滚动总和
【发布时间】:2013-10-13 08:45:36
【问题描述】:

我有一个包含datetime_spent 列的表格。我想为每个日期 D 找到一段时间内“time_spent”的值的总和:(D-7 - D),即。过去一周 + 当前日期。

我想不出办法来做到这一点,因为我只能找到总和的例子,而不是可变时间段内的总和。

这是一个数据集示例:

CREATE TABLE rolling_total
(
  date date,
  time_spent int
);

INSERT INTO rolling_total VALUES ('2013-09-01','2'),
('2013-09-02','1'),
('2013-09-03','3'),
('2013-09-04','4'),
('2013-09-05','2'),
('2013-09-06','5'),
('2013-09-07','3'),
('2013-09-08','2'),
('2013-09-09','1'),
('2013-09-10','1'),
('2013-09-11','1'),
('2013-09-12','3'),
('2013-09-13','2'),
('2013-09-14','4'),
('2013-09-15','6'),
('2013-09-16','1'),
('2013-09-17','2'),
('2013-09-18','3'),
('2013-09-19','4'),
('2013-09-20','1'),
('2013-09-21','6'),
('2013-09-22','5'),
('2013-09-23','3'),
('2013-09-24','1'),
('2013-09-25','5'),
('2013-09-26','2'),
('2013-09-27','1'),
('2013-09-28','4'),
('2013-09-29','3'),
('2013-09-30','2')

结果如下:

date       | time_spent   | rolling_week_total
2013-09-01 |           2  |         2
2013-09-02 |           1  |         3
2013-09-03 |           3  |         6
2013-09-04 |           4  |        10
2013-09-05 |           2  |        12
2013-09-06 |           5  |        17
2013-09-07 |           3  |        20
2013-09-08 |           2  |        22
// now we omit values that are older than seven days
2013-09-09 |           1  |        21
2013-09-10 |           1  |        21
...

【问题讨论】:

    标签: mysql sql sum query-optimization rolling-computation


    【解决方案1】:

    还有一个解决方案

    SELECT r1.date, r1.time_spent, sum(r2.time_spent) AS rolling_week_total
    FROM rolling_total AS r1 JOIN rolling_total AS r2
        ON datediff(r1.date, r2.date) BETWEEN 0 AND 7
    GROUP BY r1.date
    ORDER BY r1.date
    LIMIT 8
    

    【讨论】:

    • 当有重复的日期时,有什么好的方法吗?
    【解决方案2】:

    给你:

    select
    t.*,
    @total:=@total + t.time_spent - coalesce(t2.time_spent, 0) as your_total_over_last_7_days
    from
    rolling_total t
    left join rolling_total t2 on t.date = t2.date + interval 8 day
    , (select @total:=0) v
    order by t.`date` 
    
    • sqlfiddle 中实时查看它的工作情况。

    【讨论】:

      【解决方案3】:

      这是另一个解决方案

      SELECT r1.date, r1.time_spent, 
         ( SELECT SUM(time_spent) 
           FROM rolling_total AS r2
           WHERE datediff(r1.date, r2.date) 
           BETWEEN 0 AND 7
         ) AS rolling_week_total
      FROM rolling_total AS r1  
      ORDER BY r1.date
      LIMIT 8
      

      【讨论】:

      • 无意冒犯,但是您的查询在性能方面会很痛苦,因为 rolling_total 中的每一行都会执行您的子查询。
      • 是的,你是对的。我添加了一个没有这个缺点的可能的解决方案:)
      【解决方案4】:

      MySQL 8 有 window functions 专门针对这种情况:

      SELECT
          SUM(time_spent) OVER(
               ORDER BY date RANGE BETWEEN INTERVAL 7 DAY PRECEDING 
               AND CURRENT ROW) AS total,
          date
      FROM rolling_total
      

      ORDER BY 日期决定了您用于滚动窗口的维度。

      RANGE BETWEEN A AND B定义了窗口的过滤条件。

      INTERVAL 7 DAY PRECEDING 表示当前行之前的所有 7 天。

      CURRENT ROW 使用当前行的值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-17
        • 1970-01-01
        • 2021-09-12
        • 1970-01-01
        • 1970-01-01
        • 2015-03-13
        • 1970-01-01
        • 2021-05-12
        相关资源
        最近更新 更多