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 |
+------+---------------------+---------------------+-------------+
ユーザーごとに(そして週ごとに)毎日何分占有されているかを知りたいので、現在のテーブルを、部分的な時空を共有する間隔が1つにマージされる場所と、いくつかの間隔にマージされる場所に変換する必要があります日は次のように分割されます:
+------+---------------------+---------------------+-------------+
| 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 |
+------+---------------------+---------------------+-------------+
次に、1日あたりの分数を取得するために簡単にクエリするには:
+------+------------+------+
| 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 |
+------+------------+------+
私はここで複数の日付間隔をマージする方法を見つけた情報を探していました( mysql-合計間隔の日付 )が、間隔を数日に分割することはできません。
単一のクエリでそれを行うことは可能ですか?どうすればできますか?
編集:
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');
複数の間隔をマージするSQL(ソース: mysql-間隔の日付の合計 ):
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;
最初に一連の日付を生成する必要があるため、calendar
テーブルを使用することをお勧めします。
CREATE TABLE if not exists calendar (
mdate date PRIMARY KEY NOT NULL
);
INSERT INTO calendar values
('20170403'),('20170404'),('20170405'),('20170406'),('20170407'),('20170408');
彼らはそれをどのように行うのですか
重複するアクティビティを取得するために、質問で提供したクエリを使用しました。
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;
まず、開始日から真夜中までの分数を計算します。
if(date(start) = date(end),
time_to_sec(timediff(end, start)) / 60,
(1440 - time_to_sec(time(start)) / 60)) mstart
次に、開始<>終了の場合、午前0時から終了日までの分数を計算します。
if(date(start) = date(end), 0, time_to_sec(time(end)) / 60) mend
これは次のようなテーブルを返します:
| 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 |
それはいいですが、ここには別の問題があります。
| 05.04.2017 23:00:00 | 07.04.2017 00:45:00 | 1545,0000 | 60,0000 | 45,0000 |
もちろん: 1545 <> 60 + 45
開始日と終了日の間に一連の日付を生成し、1日に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
さて、すべての材料を入手しました。次はレシピを調理します。
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;
最終結果:
| 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 |
ほとんど忘れて、レシピ: http://rextester.com/EIJOI2098
日付で分割されており、DATETIMEを使用している場合は、非常に簡単です。タイムスタンプをサブストリング化し、日付でグループ化する
SELECT
who,
SUBSTRING(date_created, 1, 10) AS date,
COUNT(times) AS time
FROM timings
WHERE date_created > '2017-04-03' AND date_created < '2017-04-10'
GROUP BY date
+------+------------+------+
| 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 |
+------+------------+------+
MySQLでは、レベルごとに接続のOracle機能がMySQLでサポートされていないため、日付を分割するために、日付を持つテーブルと結合する必要があります。
詳細を追加しています...
Create table ActivityDay
(id INT Primary Key NOT NULL AUTO_INCREMENT, ActivityDay Date NOT NULL);
insert into ActivityDate (ActivityDay) values('2017-04-03'),
values('2017-04-04'),values('2017-04-05'),
values('2017-04-06'),values('2017-04-07'),
values('2017-04-08');
Select id, Name, ActualStart, ActualEnd,
(Time_to_Sec(ActualEnd) - Time_to_Sec(ActualStart))/60 time
from (
select a.id, a.name, a.start, Date(start) StartDay, Date(end) EndDay, a.time,
case when DATE(START) = DATE(ActualStart) then Start else ActivityDay end ActualStart,
case when Date(end) <> Date(ActualEnd) then date_add(ActivityDay, interval 24*60*60 - 1 second) else end end ActualEnd,
ad.ActivityDay
from Activities a join ActivityDate ad) d
Where ActivityDay between start and end)