SQL查询以分组的开始和结束分组的日期

时间:2018-08-19 20:41:30

标签: mysql sql

我的数据在下表中

+------------+----------+------------+
| 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       | 
    +------------+----------+------------+

我可以通过选择开始的最小值和结束的最大值来找到一个连续的范围,但需要如何将其分成范围的帮助。

3 个答案:

答案 0 :(得分:0)

您可以在日期表中使用CROSS JOIN,然后在日期表上使用LEFT JOIN

如果您的MySQL不支持窗口功能,则可以尝试使用EventnameAction的select子查询生成行号,以使结果从开始日期到结束日期表。

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

Results

| 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

sqlfiddle

答案 1 :(得分:0)

我可以建议followibg查询: 让我们称之为表格事件

我们需要做的第一件事是将其自身连接起来,以使起点与终点位于同一行:

    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作为确切的出现时间

答案 2 :(得分:0)

如果没有窗口功能,那将是一个麻烦……但是,通过大量的子查询,这可能是可能的。

首先CROSS JOIN将事件和日期作为事件日矩阵。

要检查事件是否正在进行,请检查是否有任何开始动作,这些动作在矩阵日的当前日期之前或之前。可以通过使用EXISTS来完成。

然后检查,是否在矩阵中的日期之前以及在矩阵中的日期之前或之日的最后一次启动操作之后没有结束动作。为此,请使用NOT EXISTS和聚合(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(请注意,我添加了一些额外的事件,这些事件仅发生了一天零一天,但尚未结束(或在给定时间段内),以证明这些情况也已涵盖在内。不过,从另一端开始是无效的,也就是说,如果只有结束动作,则不会将相应事件显示为从范围开始到结束动作发生的那一天。)