这是一种方法。
首先按时间戳顺序获取状态行(内联视图别名为s
)。然后在处理每一行时使用 MySQL 用户变量来保留前一行的值。
我们真正要寻找的是紧跟一系列“下降”状态的“上升”状态。当我们找到状态为“up”的那一行时,我们真正需要的是前一系列“down”状态中最早的时间戳。
所以,这样的事情会起作用:
SELECT d.start_down
, d.ended_down
FROM (SELECT @i := @i + 1 AS i
, @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
, @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
, @status := s.status
FROM (SELECT t.time
, t.status
FROM mydata t
WHERE t.status IN ('up','down')
ORDER BY t.time ASC, t.status ASC
) s
JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i
) d
WHERE d.start_down IS NOT NULL
AND d.ended_down IS NOT NULL
这适用于您显示的特定数据集。
这不处理的(不返回的)是一个尚未结束的“下降”时期,即一系列“下降”状态,没有后续的“上升”状态。
为避免文件排序操作按顺序返回行,您需要在(time,status)
. 此查询将生成一个临时 (MyISAM) 表来具体化别名为d
.
注意:要了解此查询在做什么,请剥离最外层的查询,然后只运行别名为的内联视图的查询d
(您可以添加s.time
到选择列表中。)
此查询正在获取具有“向上”或“向下”状态的每一行。“技巧”是它仅在结束“下降”期间的行上分配“开始”和“结束”时间(标记下降时期)。(也就是说,第一行状态为“向上”,然后是状态为“向下”的行。)这是真正完成工作的地方,最外面的查询只是过滤掉这个结果集中的所有“额外”行(我们不需要。)
SELECT @i := @i + 1 AS i
, @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
, @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
, @status := s.status
, s.time
FROM (SELECT t.time
, t.status
FROM mydata t
WHERE t.status IN ('up','down')
ORDER BY t.time ASC, t.status ASC
) s
JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i
内联视图 aliased as 的目的s
是获取按时间戳值排序的行,以便我们按顺序处理它们。内联视图别名 asi
就在那里,因此我们可以在查询开始时初始化一些用户变量。
如果我们在 Oracle 或 SQL Server 上运行,我们可以使用“分析函数”或“排名函数”(分别命名为)。MySQL 不提供类似的东西,所以我们必须“自己动手” ”。