vgc*_*vgc 6 mysql datetime interval
我有一个表,其中存储了一个活动列表,其时间间隔由 2 个日期分隔。
样本:
+------+---------------------+---------------------+-------------+
| name | start | end | time (calc) |
+------+---------------------+---------------------+-------------+
| me | 2017-04-03 11:00:00 | 2017-04-03 11:30:00 | 30 |
| me | 2017-04-03 23:45:00 | 2017-04-04 00:15:00 | 30 |
| me | 2017-04-04 10:00:00 | 2017-04-04 11:00:00 | 60 |
| me | 2017-04-04 10:30:00 | 2017-04-04 11:30:00 | 60 |
| me | 2017-04-05 23:00:00 | 2017-04-05 23:30:00 | 30 |
| me | 2017-04-05 23:15:00 | 2017-04-07 00:45:00 | 1530 |
+------+---------------------+---------------------+-------------+
Run Code Online (Sandbox Code Playgroud)
我想知道每个用户每天(然后是每周)占用多少分钟,所以我需要将当前表转换为共享部分空间时间的间隔合并为一个,以及多个间隔日子是分开的,就像下一个:
+------+---------------------+---------------------+-------------+
| name | start | end | time (calc) |
+------+---------------------+---------------------+-------------+
| me | 2017-04-03 11:00:00 | 2017-04-03 11:30:00 | 30 |
| me | 2017-04-03 23:45:00 | 2017-04-03 23:59:59 | 15 |
| me | 2017-04-04 00:00:00 | 2017-04-04 00:15:00 | 15 |
| me | 2017-04-04 10:00:00 | 2017-04-04 11:30:00 | 90 |
| me | 2017-04-05 23:00:00 | 2017-04-05 23:59:59 | 60 |
| me | 2017-04-06 00:00:00 | 2017-04-06 23:59:59 | 1440 |
| me | 2017-04-07 00:00:00 | 2017-04-07 00:45:00 | 45 |
+------+---------------------+---------------------+-------------+
Run Code Online (Sandbox Code Playgroud)
然后轻松查询它以获得每天的分钟数:
+------+------------+------+
| name | day | time |
+------+------------+------+
| me | 2017-04-03 | 45 |
| me | 2017-04-04 | 105 |
| me | 2017-04-05 | 60 |
| me | 2017-04-06 | 1440 |
| me | 2017-04-07 | 45 |
+------+------------+------+
Run Code Online (Sandbox Code Playgroud)
我正在寻找信息,我发现如何在此处合并多个日期间隔(mysql - sum interval 日期),但是我无法在几天内拆分一个间隔。
可以在单个查询中完成吗?我怎么能做到?
编辑:
SQL(结构和数据):
CREATE TABLE activities (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(45),
start DATETIME,
end DATETIME,
time INT GENERATED ALWAYS AS (TIMESTAMPDIFF(MINUTE, start, end)) VIRTUAL
);
INSERT INTO activities (name, start, end) VALUES
('me','2017-04-03 11:00','2017-04-03 11:30'),
('me','2017-04-03 23:45','2017-04-04 00:15'),
('me','2017-04-04 10:00','2017-04-04 11:00'),
('me','2017-04-04 10:30','2017-04-04 11:30'),
('me','2017-04-05 23:00','2017-04-05 23:30'),
('me','2017-04-05 23:15','2017-04-07 00:45');
Run Code Online (Sandbox Code Playgroud)
用于合并多个间隔的 SQL(来源:mysql - sum 间隔日期):
SELECT name, min(start) AS start, end, TIMESTAMPDIFF(MINUTE, MIN(start), end) AS time
FROM (
SELECT x.name, x.start, min(y.end) AS end
FROM activities AS x
JOIN activities AS y
ON x.name = y.name
AND x.start <= y.end
AND NOT EXISTS (
SELECT 1
FROM activities AS z
WHERE y.name = z.name
AND y.end >= z.start
AND y.end < z.end
)
WHERE NOT EXISTS (
SELECT 1
FROM activities AS u
WHERE x.name = u.name
AND x.start > u.start
AND x.start <= u.start
)
GROUP BY x.name, x.start
) AS v GROUP BY name, end;
Run Code Online (Sandbox Code Playgroud)
首先,由于您需要生成一系列日期,我建议使用表格calendar。
CREATE TABLE if not exists calendar (
mdate date PRIMARY KEY NOT NULL
);
INSERT INTO calendar values
('20170403'),('20170404'),('20170405'),('20170406'),('20170407'),('20170408');
Run Code Online (Sandbox Code Playgroud)
他们是如何做到的呢
为了获得重叠的活动,我使用了您在问题中提供的查询。
create view overlaped_activities
as
SELECT name, min(start) AS start, end, TIMESTAMPDIFF(MINUTE, MIN(start), end) AS time
FROM (
SELECT x.name, x.start, min(y.end) AS end
FROM activities AS x
JOIN activities AS y
ON x.name = y.name
AND x.start <= y.end
AND NOT EXISTS (
SELECT 1
FROM activities AS z
WHERE y.name = z.name
AND y.end >= z.start
AND y.end < z.end
)
WHERE NOT EXISTS (
SELECT 1
FROM activities AS u
WHERE x.name = u.name
AND x.start > u.start
AND x.start <= u.start
)
GROUP BY x.name, x.start
) AS v GROUP BY name, end;
Run Code Online (Sandbox Code Playgroud)
首先,我计算从开始日期到午夜的分钟数:
if(date(start) = date(end),
time_to_sec(timediff(end, start)) / 60,
(1440 - time_to_sec(time(start)) / 60)) mstart
Run Code Online (Sandbox Code Playgroud)
然后,如果 start <> end,我计算从午夜到结束日期的分钟数:
if(date(start) = date(end), 0, time_to_sec(time(end)) / 60) mend
Run Code Online (Sandbox Code Playgroud)
这会返回一个像这样的表:
| start | end | mdiff | mstart | mend |
|---------------------|---------------------|----------:|--------:|--------:|
| 03.04.2017 11:00:00 | 03.04.2017 11:30:00 | 30,0000 | 30,0000 | 0 |
| 03.04.2017 23:45:00 | 04.04.2017 00:15:00 | 30,0000 | 15,0000 | 15,0000 |
| 04.04.2017 10:00:00 | 04.04.2017 11:30:00 | 90,0000 | 90,0000 | 0 |
| 05.04.2017 23:00:00 | 07.04.2017 00:45:00 | 1545,0000 | 60,0000 | 45,0000 |
Run Code Online (Sandbox Code Playgroud)
这很好,但是这里还有另一个问题:
| 05.04.2017 23:00:00 | 07.04.2017 00:45:00 | 1545,0000 | 60,0000 | 45,0000 |
Run Code Online (Sandbox Code Playgroud)
当然:1545 <> 60 + 45
我们需要生成开始日期和结束日期之间的一系列日期,并为每天添加 1440 分钟。
我们可以使用日历表来获取它:
select name,
mdate date_activity,
sum(1440) minutes
from calendar
join overlaped_activities
on calendar.mdate > date(start)
and calendar.mdate < date(end)
where datediff(end, start) > 1
group by name, mdate
Run Code Online (Sandbox Code Playgroud)
好的,我们准备好了所有的原料,是时候烹饪食谱了:
select name, date_activity, sum(minutes) min_activity
from (
select name,
date(start) date_activity,
if(date(start) = date(end), time_to_sec(timediff(end, start)) / 60, (1440 - time_to_sec(time(start)) / 60)) minutes
from overlaped_activities
UNION ALL
select name,
date(end) date_activity,
if(date(start) = date(end), 0, time_to_sec(time(end)) / 60) minutes
from overlaped_activities
UNION ALL
select name,
mdate date_activity,
sum(1440) minutes
from calendar
join overlaped_activities
on calendar.mdate > date(start)
and calendar.mdate < date(end)
where datediff(end, start) > 1
group by name, mdate
) act
group by name, date_activity;
Run Code Online (Sandbox Code Playgroud)
最后结果:
| name | date_activity | min_activity |
|------|--------------------:|-------------:|
| me | 03.04.2017 00:00:00 | 45,0000 |
| me | 04.04.2017 00:00:00 | 105,0000 |
| me | 05.04.2017 00:00:00 | 60,0000 |
| me | 06.04.2017 00:00:00 | 1440,0000 |
| me | 07.04.2017 00:00:00 | 45,0000 |
Run Code Online (Sandbox Code Playgroud)
差点忘了,菜谱:http://rextester.com/EIJOI20983