首先将您的问题归结为核心:选择最近连续成功的 ping。
这可以通过仅使用包含三列的数据表来完成:(machine_id INT, insert_date INT, status ENUM("SUCC", "FAIL"))。
针对这个问题,我找到了以下解决方案:
SELECT C.machine_id, C.insert_date
FROM data AS C
WHERE C.status = "SUCC"
AND (
C.insert_date > ANY ( -- C.insert_date is later than the latest failure which occurred before the latest success.
SELECT max(A.insert_date) AS LATEST_FAILURE_BEFORE_LATEST_SUCCESS
FROM data AS A JOIN data AS B
ON (A.machine_id = B.machine_id AND A.status != "SUCC" AND B.status = "SUCC" AND A.insert_date < B.insert_date)
GROUP BY A.machine_id HAVING A.machine_id = C.machine_id
)
OR NOT EXISTS ( -- corner case NO_PRECEDING_FAILURES
SELECT 1 FROM data AS D
WHERE D.machine_id = C.machine_id
AND D.status != "SUCC"
)
);
我想复杂的部分是内部的SELECT,我在其中加入数据并选择LATEST_FAILURE_BEFORE_LATEST_SUCCESS。在这里,我利用成功 ping 的最新连续记录(通常)在它们之前有失败的事实。我选择最新的fa
带有测试用例的完整脚本:
DROP DATABASE example;
CREATE DATABASE example;
USE example;
CREATE TABLE data (
machine_id INT,
insert_date INT,
status ENUM("SUCC", "FAIL")
);
-- Test cases with different machines
INSERT INTO data (machine_id, insert_date, status) VALUES
-- 0. a machine which has sequences of FAIL - SUCC
(0, 4, "FAIL"),
(0, 3, "SUCC"), -- TARGET
(0, 2, "SUCC"), -- TARGET
(0, 1, "FAIL"),
(0, 0, "FAIL"),
-- 1. a machine which has sequences of FAIL - SUCC - FAIL
(1, 4, "FAIL"),
(1, 3, "SUCC"), -- TARGET
(1, 2, "SUCC"), -- TARGET
(1, 1, "FAIL"),
(1, 0, "FAIL"),
-- 2. a machine which has sequences of FAIL - SUCC - FAIL - SUCC
(2, 7, "SUCC"), -- TARGET
(2, 6, "SUCC"), -- TARGET
(2, 5, "SUCC"), -- TARGET
(2, 4, "FAIL"),
(2, 3, "SUCC"),
(2, 2, "SUCC"),
(2, 1, "FAIL"),
(2, 0, "FAIL"),
-- 3. a machine which has sequences of FAIL - SUCC - FAIL - SUCC - FAIL
(3, 9, "FAIL"),
(3, 8, "FAIL"),
(3, 7, "SUCC"), -- TARGET
(3, 6, "SUCC"), -- TARGET
(3, 5, "SUCC"), -- TARGET
(3, 4, "FAIL"),
(3, 3, "SUCC"),
(3, 2, "SUCC"),
(3, 1, "FAIL"),
(3, 0, "FAIL"),
-- 4. a machine which has sequences of SUCC - FAIL
(4, 2, "FAIL"),
(4, 1, "SUCC"), -- TARGET
(4, 0, "SUCC"), -- TARGET
-- 5. a machine which has sequences of SUCC - FAIL - SUCC
(5, 5, "SUCC"), -- TARGET
(5, 4, "SUCC"), -- TARGET
(5, 3, "SUCC"), -- TARGET
(5, 2, "FAIL"),
(5, 1, "SUCC"),
(5, 0, "SUCC"),
-- 6. a machine which has sequences of SUCC - FAIL - SUCC - FAIL
(6, 7, "FAIL"),
(6, 6, "FAIL"),
(6, 5, "SUCC"), -- TARGET
(6, 4, "SUCC"), -- TARGET
(6, 3, "SUCC"), -- TARGET
(6, 2, "FAIL"),
(6, 1, "SUCC"),
(6, 0, "SUCC"),
-- 7. a machine which has only FAIL
(7, 2, "FAIL"),
(7, 1, "FAIL"),
(7, 0, "FAIL"),
-- 8. a machine which has only SUCC
(8, 2, "SUCC"), -- TARGET
(8, 1, "SUCC"), -- TARGET
(8, 0, "SUCC"); -- TARGET
SELECT C.machine_id, C.insert_date
FROM data AS C
WHERE C.status = "SUCC"
AND (
C.insert_date > ANY ( -- C.insert_date is later than the latest failure which occurred before the latest success.
SELECT max(A.insert_date) AS LATEST_FAILURE_BEFORE_LATEST_SUCCESS
FROM data AS A JOIN data AS B
ON (A.machine_id = B.machine_id AND A.status != "SUCC" AND B.status = "SUCC" AND A.insert_date < B.insert_date)
GROUP BY A.machine_id HAVING A.machine_id = C.machine_id
)
OR NOT EXISTS ( -- corner case NO_PRECEDING_FAILURES
SELECT 1 FROM data AS D
WHERE D.machine_id = C.machine_id
AND D.status != "SUCC"
)
);
-- Should return:
-- machine_id insert_date
-- 0 3
-- 0 2
-- 1 3
-- 1 2
-- 2 7
-- 2 6
-- 2 5
-- 3 7
-- 3 6
-- 3 5
-- 5 5
-- 5 4
-- 5 3
-- 6 5
-- 6 4
-- 6 3
-- 8 2
-- 8 1
-- 8 0
DROP DATABASE example;