我无法解决此查询问题,以获取两个处于OFF状态的两个不同IP地址的公共持续时间。
以下示例数据中的案例
1(简单情况) - 对于00:10:10到00:20:00,IP地址“10.0.1.2”保持为OFF,在此持续时间内“10.0.1.3”也为OFF,因此常用的持续时间为OFF两者都是00:10:10到00:20:00。
2(有问题) - IP地址“10.0.1.2”在13:00:00~13:25:00关闭,如果和其他IP地址核对的话,12:55:00~13为OFF :20:00。因此,两者的共同持续时间为13:00:00至13:20:00。
示例数据:
ID IP address Status Time
----------------------------------
1 10.0.1.2 OFF 00:10:00
1 10.0.1.2 ON 00:20:00
1 10.0.1.2 OFF 11:00:00
1 10.0.1.2 ON 11:20:00
1 10.0.1.2 OFF 13:00:00
1 10.0.1.2 ON 13:25:00
1 10.0.1.2 OFF 14:05:00
1 10.0.1.2 ON 14:10:00
1 10.0.1.2 OFF 15:35:00
1 10.0.1.2 ON 15:45:00
1 10.0.1.3 OFF 00:10:00
1 10.0.1.3 ON 00:20:00
1 10.0.1.3 OFF 11:05:00
1 10.0.1.3 ON 11:25:00
1 10.0.1.3 OFF 12:55:00
1 10.0.1.3 ON 13:20:00
1 10.0.1.3 OFF 17:10:00
1 10.0.1.3 ON 17:15:00
1 10.0.1.3 OFF 15:00:00
1 10.0.1.3 ON 16:45:00
输出:
ID IP addresses Status Time
-----------------------------------------
1 10.0.1.3,10.0.1.2 OFF 00:10:00
1 10.0.1.3,10.0.1.2 ON 00:20:00
1 10.0.1.3,10.0.1.2 OFF 11:05:00
1 10.0.1.3,10.0.1.2 ON 11:20:00
1 10.0.1.3,10.0.1.2 OFF 13:00:00
1 10.0.1.3,10.0.1.2 ON 13:20:00
1 10.0.1.3,10.0.1.2 OFF 15:35:00
1 10.0.1.3,10.0.1.2 ON 15:45:00
答案 0 :(得分:5)
这是你的首发。
ip
。CREATE TABLE foo (ip int NOT NULL, status text NOT NULL,
ts time NOT NULL, PRIMARY KEY (ip, status, ts));
INSERT INTO foo VALUES
(2, 'OFF', '00:10:00'),
(2, 'ON', '00:20:00'),
(2, 'OFF', '11:00:00'),
(2, 'ON', '11:20:00'),
(2, 'OFF', '13:00:00'),
(2, 'ON', '13:25:00'),
(2, 'OFF', '14:05:00'),
(2, 'ON', '14:10:00'),
(2, 'OFF', '15:35:00'),
(2, 'ON', '15:45:00'),
(3, 'OFF', '00:10:00'),
(3, 'ON', '00:20:00'),
(3, 'OFF', '11:05:00'),
(3, 'ON', '11:25:00'),
(3, 'OFF', '12:55:00'),
(3, 'ON', '13:20:00'),
(3, 'OFF', '17:10:00'),
(3, 'ON', '17:15:00'),
(3, 'OFF', '15:00:00'),
(3, 'ON', '16:45:00');
假设您在MySQL中使用了公用表表达式CTE(除此之外,您没有指定版本)。
如果您没有CTE,那么只需复制并替换CTE的所有引用(在本例中为off
)并为其命名。
最后一个示例不会使用WITH
。
WITH off AS
(SELECT ip,
ts "off_from",
(SELECT ts FROM foo
WHERE ip = a.ip AND a.ts <= ts AND status = 'ON'
ORDER BY ts ASC LIMIT 1) "off_until"
FROM foo a WHERE status = 'OFF'
)
SELECT * FROM off;
哪个给出了
ip | off_from | off_until
----+----------+-----------
2 | 00:10:00 | 00:20:00
2 | 11:00:00 | 11:20:00
2 | 13:00:00 | 13:25:00
2 | 14:05:00 | 14:10:00
2 | 15:35:00 | 15:45:00
3 | 00:10:00 | 00:20:00
3 | 11:05:00 | 11:25:00
3 | 12:55:00 | 13:20:00
3 | 17:10:00 | 17:15:00
3 | 15:00:00 | 16:45:00
WITH off AS
(SELECT ip,
ts "off_from",
(SELECT ts FROM foo
WHERE ip = a.ip AND a.ts <= ts AND status = 'ON'
ORDER BY ts ASC LIMIT 1) "off_until"
FROM foo a WHERE status = 'OFF'
)
SELECT *
FROM off x
INNER JOIN off y
ON x.off_from <= y.off_from AND y.off_from < x.off_until
AND x.ip <> y.ip ;
ip | off_from | off_until | ip | off_from | off_until
----+----------+-----------+----+----------+-----------
2 | 00:10:00 | 00:20:00 | 3 | 00:10:00 | 00:20:00
2 | 11:00:00 | 11:20:00 | 3 | 11:05:00 | 11:25:00
3 | 00:10:00 | 00:20:00 | 2 | 00:10:00 | 00:20:00
3 | 12:55:00 | 13:20:00 | 2 | 13:00:00 | 13:25:00
3 | 15:00:00 | 16:45:00 | 2 | 15:35:00 | 15:45:00
要获得最小和最长时间使用
WITH off AS
(SELECT ip,
ts "off_from",
(SELECT ts FROM foo
WHERE ip = a.ip AND a.ts <= ts AND status = 'ON'
ORDER BY ts ASC LIMIT 1) "off_until"
FROM foo a WHERE status = 'OFF'
)
SELECT x.ip "ip_a", y.ip "ip_b",
greatest( x.off_from, y.off_from ) "off_from",
least( x.off_until, y.off_until ) "off_until"
FROM off x
INNER JOIN off y
ON x.off_from <= y.off_from AND y.off_from < x.off_until
AND x.ip <> y.ip ;
产生
ip_a | ip_b | off_from | off_until
------+------+----------+-----------
2 | 3 | 00:10:00 | 00:20:00
2 | 3 | 11:05:00 | 11:20:00
3 | 2 | 00:10:00 | 00:20:00
3 | 2 | 13:00:00 | 13:20:00
3 | 2 | 15:35:00 | 15:45:00
没有WITH
(复制粘贴并命名CTE)。
SELECT x.ip "ip_a", y.ip "ip_b",
greatest( x.off_from, y.off_from ) "off_from",
least( x.off_until, y.off_until ) "off_until"
FROM
(SELECT ip,
ts "off_from",
(SELECT ts
FROM foo
WHERE ip = a.ip AND a.ts <= ts AND status = 'ON'
ORDER BY ts ASC LIMIT 1) "off_until"
FROM foo a WHERE status = 'OFF'
) x
INNER JOIN
(SELECT ip,
ts "off_from",
(SELECT ts
FROM foo
WHERE ip = a.ip AND a.ts <= ts AND status = 'ON'
ORDER BY ts ASC LIMIT 1) "off_until"
FROM foo a WHERE status = 'OFF'
) y
ON x.off_from <= y.off_from
AND y.off_from < x.off_until
AND x.ip <> y.ip ;
对于LIMIT 1
的内部选择,请考虑(ip, status, ts)
上的索引。
对于连接,您的DBMS可能会使用ts
上的索引。 CTE(WITH
子句)仅实现一次虚拟表。这可能不适用于复制粘贴CTE几次(这里是两次)。
这对你来说应该是一个粗略的启动。到目前为止还不是完美或最好的解决方案。可能还有其他更好的。
答案 1 :(得分:2)
一种方法是使用TIME_TO_SEC()
将时间作为秒,并计算存储过程中的差异:
Create table common_duration (
ip varchar (10),
start_time time,
end_time time
)
CREATE PROCEDURE `comm_time`()
BEGIN
DECLARE curs1 CURSOR FOR SELECT `IP`, TIME_TO_SEC(`time`) as time, STATUS FROM TABLE;
DECLARE ip varchar(20);
DECLARE iptime time;
DECLARE ipstime time;
DECLARE ipstatus varchar(10);
OPEN curs1;
FETCH curs1 INTO ip,iptime,ipstatus;
if (status='ON')
insert into `common_duration`(ip, start_time, end_time) values(ip, ipstime, iptime);
else
ipstime=iptime;
endif;
CLOSE curs1;
SELECT t1.ip SEC_TO_TIME(t1.end_time-t1.start_time) as time_duration FROM `common_duration t1, `common_duration t2
WHERE t1.time_duration= t2.time_duration
AND t1.ip != t2.ip;
End