首页 > 数据库技术 > 详细

用sql对含有时间段字段(起始时间、结束时间)的记录做并集处理

时间:2020-03-29 00:00:44      阅读:267      评论:0      收藏:0      [点我收藏+]

来自于一个基友的问题:

他的博客同问题链接    sql时间段取并集、合并 https://blog.csdn.net/Seandba/article/details/105152412 

计算通道的总开放时长,只要有任意一个终端开放通道就算开放,难点在于各种终端开放时间重叠包含

技术分享图片

问题测试数据:

--问题一、测试数据--计算总开放时长(小时)
TRUNCATE TABLE xcp;
insert into xcp values(‘1‘,‘A1‘,to_date(‘20200317 01:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 06:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘2‘,‘A1‘,to_date(‘20200317 01:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 06:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘2‘,‘A1‘,to_date(‘20200317 01:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 08:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘2‘,‘A1‘,to_date(‘20200317 02:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 07:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘2‘,‘A1‘,to_date(‘20200317 03:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 07:00:00‘,‘yyyymmdd hh24:mi:ss‘));

insert into xcp values(‘2‘,‘A1‘,to_date(‘20200317 05:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 09:00:00‘,‘yyyymmdd hh24:mi:ss ‘));
insert into xcp values(‘3‘,‘A1‘,to_date(‘20200317 09:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 11:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘3‘,‘A1‘,to_date(‘20200317 12:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 13:00:00‘,‘yyyymmdd hh24:mi:ss‘));

insert into xcp values(‘2‘,‘A1‘,to_date(‘20200317 14:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 19:00:00‘,‘yyyymmdd hh24:mi:ss ‘));
insert into xcp values(‘3‘,‘A1‘,to_date(‘20200317 16:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 19:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘3‘,‘A1‘,to_date(‘20200317 18:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 19:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘3‘,‘A1‘,to_date(‘20200317 18:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 21:00:00‘,‘yyyymmdd hh24:mi:ss‘));
commit;

SELECT * FROM xcp;

技术分享图片问题核心是求多条记录之间的并集操作 ,我写的sql如下,

--问题1
WITH tmp1 AS (  --取所有时间节点
SELECT channel,BEGIN_TIME TIME FROM xcp
UNION SELECT channel,end_time FROM xcp
UNION SELECT channel,MIN(begin_time) FROM xcp GROUP BY channel
UNION SELECT channel,MAX(end_time) FROM xcp GROUP BY channel),

tmp2 AS(--每个时间节点连接到下个节点  形成时间段
SELECT a.channel,a.time,LEAD(a.time,1) OVER(PARTITION BY a.channel ORDER BY a.time) nexttime
FROM tmp1 a),

tmp3 AS(--每个时间段取中值
SELECT b.channel,b.TIME,b.nexttime,(b.nexttime-b.time)/2+b.time midtime
FROM tmp2 b
WHERE b.nexttime IS NOT NULL),

tmp4 AS(--若中值处于原始记录中  则该段时间为通道开通时间 否则通道不开通
SELECT c.*,
CASE WHEN EXISTS (SELECT 1 FROM xcp o WHERE c.midtime BETWEEN o.begin_time AND o.end_time) THEN 1 ELSE 0 END *
(c.nexttime-c.time)*24 duration
FROM tmp3 c)

SELECT nvl(d.channel,‘合计时长‘) 通道,d.TIME 开始时间,d.nexttime 结束时间,
SUM(duration) "通道开通时间(小时)" FROM tmp4 d
GROUP BY rollup((d.channel,d.TIME,d.nexttime))
ORDER BY 2;

技术分享图片看着就很垃圾的sql,执行计划一定垃圾,记录以备后查询吧

原理是吧时间节点拿出来,对没两个时间节点之间的时间段,取中间值到原始记录表查询,如果是,这段时间就是属于并集后的,然后对并集后的记录求和


问题2:求17日的的通道开放时长

--问题2、测试数据--计算27号开放时长(小时)
TRUNCATE TABLE xcp;
insert into xcp values(‘13‘,‘A1‘,to_date(‘20200314 08:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200315 09:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘14‘,‘A1‘,to_date(‘20200317 08:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 09:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘15‘,‘A1‘,to_date(‘20200316 03:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200317 05:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘16‘,‘A1‘,to_date(‘20200317 08:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200318 10:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘17‘,‘A1‘,to_date(‘20200316 08:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200318 10:00:00‘,‘yyyymmdd hh24:mi:ss‘));
insert into xcp values(‘18‘,‘A1‘,to_date(‘20200320 08:00:00‘,‘yyyymmdd hh24:mi:ss‘),to_date(‘20200321 10:00:00‘,‘yyyymmdd hh24:mi:ss‘));
commit;

SELECT * FROM xcp ORDER BY begin_time

技术分享图片sql如下:

----问题2
WITH tmp1 AS (  --取所有时间节点    取17号就加入17号0点和24点两个时间
SELECT channel,BEGIN_TIME TIME FROM xcp
UNION SELECT channel,end_time FROM xcp
UNION SELECT channel,MIN(begin_time) FROM xcp GROUP BY channel
UNION SELECT channel,MAX(end_time) FROM xcp GROUP BY channel
UNION SELECT DISTINCT channel,to_date(‘20200317‘,‘yyyymmdd‘) FROM xcp
UNION SELECT DISTINCT channel,to_date(‘20200318‘,‘yyyymmdd‘) FROM xcp),

tmp2 AS(--每个时间节点连接到下个节点  形成时间段
SELECT a.channel,a.time,LEAD(a.time,1) OVER(PARTITION BY a.channel ORDER BY a.time) nexttime
FROM tmp1 a),

tmp3 AS(--每个时间段取中值
SELECT b.channel,b.TIME,b.nexttime,(b.nexttime-b.time)/2+b.time midtime
FROM tmp2 b
WHERE b.nexttime IS NOT NULL
AND to_char(b.TIME,‘yyyymmdd‘)=20200317),

tmp4 AS(--若中值处于原始记录中  则该段时间为通道开通时间 否则通道不开通
SELECT c.*,
CASE WHEN EXISTS (SELECT 1 FROM xcp o WHERE c.midtime BETWEEN o.begin_time AND o.end_time) THEN 1 ELSE 0 END *
(c.nexttime-c.time)*24 duration
FROM tmp3 c)

SELECT nvl(d.channel,‘合计时长‘) 通道,d.TIME 开始时间,d.nexttime 结束时间,
SUM(duration) "通道开通时间(小时)" FROM tmp4 d
GROUP BY rollup((d.channel,d.TIME,d.nexttime))
ORDER BY 2;

技术分享图片思路是在第一步取时间节点的时候单独加入17日0点24点的时间点即可

用sql对含有时间段字段(起始时间、结束时间)的记录做并集处理

原文:https://www.cnblogs.com/yongestcat/p/12590154.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!