【问题标题】:SQL - Is there a way to calculate a running total on a series of dates?SQL - 有没有办法计算一系列日期的累计?
【发布时间】:2019-08-08 14:45:54
【问题描述】:

给定一个事件列表,其中 1 表示某事(例如学生)加入了一个组,-1 表示某事离开了一个组,是否可以在 SQL 中按日期计算组大小?我的代码可以生成一个范围内的所有日期......当我自己运行它时可以工作。然后我想按班级参加注册活动,并有每个日期注册的总数。 (+1 == 增课,-1 == 减课)。

我认为我遗漏了一些关于 SQL 中的联接和分组如何工作的基本知识。

http://sqlfiddle.com/#!9/e4835/5/0

样本数据:

CREATE TABLE classes(`id` int, `name` varchar(7));
INSERT INTO classes(`id`, `name`) VALUES
    (1, 'math'),
    (2, 'english'),
    (3, 'sciene');

CREATE TABLE enrollment_changes(
  `class_id` int, 
  `change_date` date, 
  `change` int);
INSERT INTO enrollment_changes
    (`class_id`, `change_date`, `change`)
VALUES
    (1, '2019-01-01', 1),
    (1, '2019-01-01', 1),
    (1, '2019-01-02', -1),
    (3, '2019-01-02', 1),
    (1, '2019-01-03', 1),
    (2, '2019-01-03', -1)
;
-- This gets me part way there... it produces the product of dates x classes 
SELECT 
  date_range.event_date, c.name
FROM 
  (SELECT adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) event_date 
    FROM
      (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
      (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
      (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
      (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
      (SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
  ) AS date_range
  JOIN 
    classes c
WHERE 
  date_range.event_date BETWEEN '2019-01-01' AND '2019-01-03'
;

-- This does not work at all... it reduces the output to a single record.
SELECT 
  date_range.event_date, c.name, SUM(e.change) AS 'NetEnrollment'
FROM 
  (SELECT adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) event_date 
    FROM
      (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
      (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
      (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
      (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
      (SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
  ) AS date_range
  JOIN 
    classes c
  JOIN 
    enrollment_changes AS e
  ON
    e.change_date <= date_range.event_date
  AND
    e.class_id = c.id
WHERE 
  date_range.event_date BETWEEN '2019-01-01' AND '2019-01-03'
;

当前结果:

event_date   name   NetEnrollment
------------------------------------
2019-01-01   math      6

想要的结果:

event_date   name      NetEnrollment
------------------------------------
2019-01-01   math      1
2019-01-01   english   0
2019-01-01   science   0
2019-01-02   math      1
2019-01-02   english   1
2019-01-02   science   0
2019-01-03   math      2
2019-01-03   english   1
2019-01-03   science   1

【问题讨论】:

  • 不确定我是否理解想要的结果。为什么2019-01-03 math 2?那里的 NetEnrollment 的 2 值很奇怪,因为“同一组”2019-01-03 english 1 2019-01-03 science 1 仍然是 1 对于 NetEnrollment 我也期望那里有 2 englishscience 也有以前的值,就像 math 有?
  • 第 1 天,数学的净入学人数为 1。第 2 天,数学没有变化,因此净入学人数保持 1。第 3 天,增加了 1 名学生,净入学人数为2. 在我最初的帖子中,我省略了 AND 以将注册事件与班级联系起来,这让事情变得混乱。
  • 现在我明白了,但它不适用于当前数据集,因为在 class_id "groups" 中的 change_date 列中的重复值的情况下没有定义的顺序..

标签: mysql join cumulative-sum


【解决方案1】:

您可以使用以下解决方案:

SELECT date_range.event_date, c.name, IFNULL(SUM(e.change), 0) AS 'NetEnrollment'
FROM (
  SELECT adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) event_date 
  FROM
    (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
    (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
    (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
    (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
    (SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
) AS date_range JOIN classes c 
  LEFT JOIN enrollment_changes AS e ON e.change_date <= date_range.event_date AND  c.id = e.class_id
WHERE date_range.event_date BETWEEN '2019-01-01' AND '2019-01-03'
GROUP BY date_range.event_date, c.name 
ORDER BY date_range.event_date, c.name

demo on dbfiddle.uk


日历表上的每一天都加入所有课程(使用JOIN)。注册更改加入(使用LEFT JOIN)到当天和特定班级。使用IFNULL,您可以将NULL 值替换为0。

仅在当天使用GROUP BY 会导致每天一行。要每天为每节课排行,您必须GROUP BY 日期和班级名称。

【讨论】:

  • 成功了!我现在明白了。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 1970-01-01
  • 2022-01-14
  • 2021-12-15
相关资源
最近更新 更多