我有下表中的数据
+------------+----------+------------+
| Event name |Date |Action |
+------------+----------+------------+
| Event A |10/08/2018| Started |
| Event B |10/08/2018| Started |
| Event A |11/08/2018| Ended |
| Event B |12/08/2018| Ended |
| Event A |13/08/2018| Started |
| Event A |14/08/2018| Ended |
+------------+----------+------------+
我正在尝试编写一个查询,我想列出事件正在进行的所有日期。
+------------+----------+------------+
| Event name |Date |Status |
+------------+----------+------------+
| Event A |10/08/2018| Ongoing |
| Event A |11/08/2018| Ongoing |
| Event A |12/08/2018| null |
| Event A |13/08/2018| Ongoing |
| Event A |14/08/2018| Ongoing |
| Event A |15/08/2018| null |
| Event B |10/08/2018| Ongoing |
| Event B |11/08/2018| Ongoing |
| Event B |12/08/2018| Ongoing |
| Event B |13/08/2018| null |
| Event B |14/08/2018| null |
| Event B |15/08/2018| null |
+------------+----------+------------+
我能够通过开始和最大结束时找到一个连续范围,但需要帮助如何将其分解为范围。
我可以建议以下查询:让我们调用你的表事件
我们需要做的第一件事就是将它加入到自身中,以便在同一行开始作为结束:
SELECT * FROM events st
INNER JOIN events end ON st.event = end.event AND end.status = 'ended' AND event.date < end. date
WHERE st.status = 'started'
接下来,我们希望获得最早的结束,对于每个开始,我们将添加另一个左连接,过滤掉不是,按日期差异和NULL我们将只获得正在进行的情况:
SELECT st.event_name, st.date AS start_date, end.date AS end_date FROM events st
INNER JOIN events end ON st.event_name = end.event_name AND end.status = 'ended' AND event.date < end.date
LEFT JOIN events er ON er.event = end.event_name AND er.status = 'ended'
AND er.date < end.date
WHERE st.status = 'started'
AND er.event_name IS NULL
最后,我们加入我们写入原始表的查询并获得所需的输出:
SELECT events.event_name, events.date, IF(ong.event_name IS NULL, 'not active', 'ongoing') AS status FROM events
LEFT JOIN (SELECT st.event_name, st.date AS start_date, end.date AS end_date FROM events st
INNER JOIN events end ON st.event_name = end.event_name AND end.status = 'ended' AND event.date < end.date
LEFT JOIN events er ON er.event = end.event_name AND er.status = 'ended'
AND er.date < end.date
WHERE st.status = 'started'
AND er.event_name IS NULL) ong ON
ong.event_name = events.event_name AND ong.start_date >= events.date AND events.date <= ong.end_date
请注意,只要相同的事件不在相同的日期开始和结束,这将起作用。如果您的表保存完全出现的DATETIME,则它最佳
您可以在日期表中使用CROSS JOIN
,然后使用LEFT JOIN
。
如果你的mysql不支持窗口函数,你可以尝试使用Eventname
和Action
选择子查询make行号,让结果使开始日期到结束日期表。
CREATE TABLE T(
Eventname varchar(50),
Date date,
Action varchar(50)
);
INSERT INTO T VALUES ('Event A' ,'2018/08/10','Started');
INSERT INTO T VALUES ('Event A' ,'2018/08/11','Ended');
INSERT INTO T VALUES ('Event B' ,'2018/08/10','Started');
INSERT INTO T VALUES ('Event B' ,'2018/08/12','Ended');
INSERT INTO T VALUES ('Event A' ,'2018/08/13','Started');
INSERT INTO T VALUES ('Event A' ,'2018/08/14','Ended');
create table cT(
Date date
);
INSERT INTO cT VALUES ('2018/08/10');
INSERT INTO cT VALUES ('2018/08/11');
INSERT INTO cT VALUES ('2018/08/12');
INSERT INTO cT VALUES ('2018/08/13');
INSERT INTO cT VALUES ('2018/08/14');
INSERT INTO cT VALUES ('2018/08/15');
查询1:
SELECT
t1.Eventname,
ct.Date,
CASE WHEN t2.Action IS NOT NULL THEN 'Oppening' ELSE NULL END dt
FROM
(SELECT DISTINCT Eventname FROM T ) t1 CROSS JOIN CT ct
LEFT JOIN (
SELECT t1.Eventname,
t1.date 'startdate',
t2.date 'enddate',
t1.Action
FROM (
SELECT *,(SELECT COUNT(*)
FROM T tt
where tt.date <= t1.date and tt.Eventname = t1.Eventname and tt.Action = t1.Action) rn
FROM T t1
where Action = 'Started'
) t1 INNER JOIN
(
SELECT *,(SELECT COUNT(*)
FROM T tt
where tt.date <= t1.date and tt.Eventname = t1.Eventname and tt.Action = t1.Action) rn
FROM T t1
where Action = 'Ended'
) t2 on t1.rn = t2.rn and t1.Eventname = t2.Eventname
) t2 on ct.Date BETWEEN t2.startdate AND t2.enddate and t1.Eventname = t2.Eventname
WHERE ct.Date between '2018/08/10' and '2018/08/15'
ORDER BY t1.Eventname,ct.Date
| Eventname | Date | dt |
|-----------|------------|----------|
| Event A | 2018-08-10 | Oppening |
| Event A | 2018-08-11 | Oppening |
| Event A | 2018-08-12 | (null) |
| Event A | 2018-08-13 | Oppening |
| Event A | 2018-08-14 | Oppening |
| Event A | 2018-08-15 | (null) |
| Event B | 2018-08-10 | Oppening |
| Event B | 2018-08-11 | Oppening |
| Event B | 2018-08-12 | Oppening |
| Event B | 2018-08-13 | (null) |
| Event B | 2018-08-14 | (null) |
| Event B | 2018-08-15 | (null) |
如果支持窗口功能,您可以使用row_number
来制作它。
SELECT
t1.Eventname,
ct.Date,
CASE WHEN t2.Action IS NOT NULL THEN 'Oppening' ELSE NULL END dt
FROM
(SELECT DISTINCT Eventname FROM T ) t1 CROSS JOIN cT ct
LEFT JOIN (
SELECT t1.Eventname,
t1.date 'startdate',
t2.date 'enddate',
t1.Action
FROM (
SELECT *,ROW_NUMBER() OVER (PARTITION BY Eventname,Action ORDER BY date) rn
FROM T t1
where Action = 'Started'
) t1 INNER JOIN
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY Eventname,Action ORDER BY date) rn
FROM T t1
where Action = 'Ended'
) t2 on t1.rn = t2.rn and t1.Eventname = t2.Eventname
) t2 on ct.Date BETWEEN t2.startdate AND t2.enddate and t1.Eventname = t2.Eventname
WHERE ct.Date between '2018/08/10' and '2018/08/15'
ORDER BY t1.Eventname,ct.Date
没有窗口功能,这是一个痛苦的对接......但它可能有广泛的subquerying。
首先CROSS JOIN
事件和有你的事件日矩阵的日子。
要检查事件是否正在进行,请检查是否有任何启动操作,即矩阵日当前或之前的操作。这可以通过使用EXISTS
来完成。
然后检查,如果在矩阵中的日期之前没有结束动作,并且在矩阵之前或当天的最后一次开始动作之后。为此,请使用NOT EXISTS
和aggregation(max()
),后者将获得最后的启动操作。
SELECT e1.eventname,
d1.date,
CASE
WHEN EXISTS (SELECT *
FROM event e2
WHERE e2.eventname = e1.eventname
AND e2.date <= d1.date
AND e2.action = 'Started')
AND NOT EXISTS (SELECT *
FROM event e2
WHERE e2.eventname = e1.eventname
AND e2.date < d1.date
AND e2.date >= (SELECT max(e3.date)
FROM event e3
WHERE e3.eventname = e1.eventname
AND e3.date <= d1.date
AND e3.action = 'Started')
AND e2.action = 'Ended') THEN
'Ongoing'
END status
FROM (SELECT DISTINCT eventname
FROM event) e1
CROSS JOIN date d1
ORDER BY e1.eventname,
d1.date;
SQL Fiddle(注意,我添加了一些额外的事件,仅持续一天和一个,尚未结束(或在给定的时间段内),以证明这些案例也被覆盖。它不会起作用于另一端但是,如果只有一个结束动作,它将不会将相应的事件显示为从范围的开始到结束动作发生的那一天。)