SQL - 有没有办法计算一系列日期的 运行 总数?
SQL - Is there a way to calculate a running total on a series of dates?
给定一个带有日期的事件列表,其中 1 表示某个人(例如学生)加入了一个小组,-1 表示某个人离开了一个小组,是否可以在 SQL 中按日期计算小组规模?我有代码可以生成一个范围内的所有日期......当我 运行 它自己工作时。然后我想在 class 之前参加注册活动,并获得每个日期注册的总人数。 (+1 == class 添加,-1 == class 删除)。
我想我遗漏了一些关于连接和分组在 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
您可以使用以下解决方案:
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
日历上的每一天 table 与所有 class 一起加入(使用 JOIN
)。注册更改加入(使用 LEFT JOIN
)到当天和特定的 class。使用 IFNULL
您可以将 NULL
值替换为 0.
仅在当天使用 GROUP BY
会导致每天一行。要每天每 class 获得一行,您必须 GROUP BY
天和 class 姓名。
给定一个带有日期的事件列表,其中 1 表示某个人(例如学生)加入了一个小组,-1 表示某个人离开了一个小组,是否可以在 SQL 中按日期计算小组规模?我有代码可以生成一个范围内的所有日期......当我 运行 它自己工作时。然后我想在 class 之前参加注册活动,并获得每个日期注册的总人数。 (+1 == class 添加,-1 == class 删除)。
我想我遗漏了一些关于连接和分组在 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
您可以使用以下解决方案:
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
日历上的每一天 table 与所有 class 一起加入(使用 JOIN
)。注册更改加入(使用 LEFT JOIN
)到当天和特定的 class。使用 IFNULL
您可以将 NULL
值替换为 0.
仅在当天使用 GROUP BY
会导致每天一行。要每天每 class 获得一行,您必须 GROUP BY
天和 class 姓名。