当前位置: 首页 > news >正文

Oracle SQL - 合并重叠的期间

  • 数据和目标

有如下数据存储了各组件的有效期间(此处起止日期用数字代替以便查阅),目标为将有重叠的期间合并到一起。

SQL> SELECT * FROM demo_eff_periods;COMPONENT_ITEM_ID EFFECTIVITY_DATE DISABLE_DATE
----------------- ---------------- ------------1             1000         19991             1500         30001             3000         39991             4000         49992             1000         49992             2000         39993             1000         19993             1500         29993             1000         39994             1000         19994             2000         29994             3000         39995             1000         19995             1000         19995             4000         499915 rows selected

将数据用下图表示,除组件1的第二段区间为闭区间,其它均为左闭右开。

  •  例程一:转化为连续日期合并

先使用递归语法把每段期间拆解出具体的每一天,再将连续的日期整合为新区间。

这里使用dense_rank是因为可能有重复的日期出现。

如果出现上一期间的结束日期与下一期间的开始日期刚好连续的情况,这个方法将会把这两个区间合并到一起。

SQL> SELECT component_item_id,2         MIN(new_date) effectivity_date,3         MAX(new_date) disable_date4    FROM (SELECT dep.component_item_id,5                 dep.effectivity_date + LEVEL - 1 new_date,6                 dense_rank() over(PARTITION BY dep.component_item_id ORDER BY dep.effectivity_date + LEVEL) dr7            FROM demo_eff_periods dep8          CONNECT BY LEVEL + dep.effectivity_date - 1 <= dep.disable_date9                 AND PRIOR dep.rowid = dep.rowid10                 AND PRIOR sys_guid() IS NOT NULL)11   GROUP BY component_item_id, new_date - dr;COMPONENT_ITEM_ID EFFECTIVITY_DATE DISABLE_DATE
----------------- ---------------- ------------1             1000         49992             1000         49993             1000         39994             1000         39995             1000         19995             4000         49996 rows selected
  • 例程二:递归串联区间合并

使用RSF语法,先找到所有不被其它区间包含的期间开始日期,再递归串联后续区间,最终得以合并。

将SQL中注释的部分解除,则可实现如例程一的进一步合并连续区间的效果。

SQL> WITH rsf_eff_periods(component_item_id, effectivity_date, disable_date) AS2   (SELECT dep.component_item_id, dep.effectivity_date, dep.disable_date3      FROM demo_eff_periods dep4     WHERE NOT EXISTS5     (SELECT 16              FROM demo_eff_periods dep07             WHERE dep0.component_item_id = dep.component_item_id8               AND dep0.effectivity_date < dep.effectivity_date9               AND dep0.disable_date >= dep.effectivity_date /*- 1*/)10    UNION ALL11    SELECT rep.component_item_id, rep.effectivity_date, dep.disable_date12      FROM rsf_eff_periods rep, demo_eff_periods dep13     WHERE rep.component_item_id = dep.component_item_id14       AND rep.disable_date >= dep.effectivity_date /*- 1*/15       AND rep.disable_date < dep.disable_date)16  SELECT component_item_id, effectivity_date, MAX(disable_date) disable_date17    FROM rsf_eff_periods18   GROUP BY component_item_id, effectivity_date;COMPONENT_ITEM_ID EFFECTIVITY_DATE DISABLE_DATE
----------------- ---------------- ------------1             1000         39991             4000         49992             1000         49993             1000         39994             1000         19994             2000         29994             3000         39995             1000         19995             4000         49999 rows selected

 CONNECT BY写法:

SELECT component_item_id, effectivity_date, MAX(disable_date) disable_dateFROM (SELECT dep.component_item_id,connect_by_root effectivity_date effectivity_date,dep.disable_dateFROM demo_eff_periods depSTART WITH NOT EXISTS(SELECT 1FROM demo_eff_periods dep0WHERE dep0.component_item_id = dep.component_item_idAND dep0.effectivity_date < dep.effectivity_dateAND dep0.disable_date >= dep.effectivity_date /*- 1*/)CONNECT BY PRIOR dep.component_item_id = dep.component_item_idAND PRIOR dep.disable_date >= dep.effectivity_date /*- 1*/AND PRIOR dep.disable_date < dep.disable_date)GROUP BY component_item_id, effectivity_date;
  • 例程三:区间端点排序重组

分别找到不被其它期间包含的开始日期和结束日期,即合并后应得的区间端点,两类端点(开始和结束)分别排序,同位次的端点即组成新区间。

这里使用dense_rank是因为可能有重复的日期出现。

将SQL中注释的部分解除,则可实现如例程一的进一步合并连续区间的效果。

SQL> SELECT component_item_id,2         MIN(new_date) effectivity_date,3         MAX(new_date) disable_date4    FROM (SELECT dep.component_item_id,5                 dep.effectivity_date new_date,6                 dense_rank() over(PARTITION BY dep.component_item_id ORDER BY dep.effectivity_date) dr7            FROM demo_eff_periods dep8           WHERE NOT EXISTS9           (SELECT 110                    FROM demo_eff_periods dep011                   WHERE dep0.component_item_id = dep.component_item_id12                     AND dep0.effectivity_date < dep.effectivity_date13                     AND dep0.disable_date >= dep.effectivity_date /*- 1*/)14          UNION ALL15          SELECT dep.component_item_id,16                 dep.disable_date new_date,17                 dense_rank() over(PARTITION BY dep.component_item_id ORDER BY dep.disable_date) dr18            FROM demo_eff_periods dep19           WHERE NOT EXISTS20           (SELECT 121                    FROM demo_eff_periods dep022                   WHERE dep0.component_item_id = dep.component_item_id23                     AND dep0.effectivity_date <= dep.disable_date /*+ 1*/24                     AND dep0.disable_date > dep.disable_date))25   GROUP BY component_item_id, dr;COMPONENT_ITEM_ID EFFECTIVITY_DATE DISABLE_DATE
----------------- ---------------- ------------1             1000         39991             4000         49992             1000         49993             1000         39994             1000         19994             2000         29994             3000         39995             1000         19995             4000         49999 rows selected
  • 例程四:区间端点排序重组(减少扫描表次数)

对于每一个原始期间,如有比自己早开始的期间其结束日期比自己的开始日期还大,则自己的开始日期将被吞并;如有比自己晚结束的期间其开始日期比自己的结束日期还小,则自己的结束日期将被吞并。

筛选出合并后应得的区间端点后,两类端点(开始和结束)分别排序,同位次的端点即组成新区间。

这里使用dense_rank是因为可能有重复的日期出现。

将SQL中注释的部分解除,则可实现如例程一的进一步合并连续区间的效果。

SQL> SELECT component_item_id,2         MIN(new_date) effectivity_date,3         MAX(new_date) disable_date4    FROM (SELECT component_item_id,5                 new_date,6                 dense_rank() over(PARTITION BY component_item_id, date_pos ORDER BY new_date) dr7            FROM (SELECT dep.component_item_id,8                         CASE9                           WHEN MAX(dep.disable_date)10                            over(PARTITION BY dep.component_item_id ORDER BY11                                     dep.effectivity_date12                                     RANGE BETWEEN unbounded preceding AND 113                                     preceding) >= dep.effectivity_date /*- 1*/ THEN14                            NULL15                           ELSE16                            dep.effectivity_date17                         END AS effectivity_date,18                         CASE19                           WHEN MIN(dep.effectivity_date)20                            over(PARTITION BY dep.component_item_id ORDER BY21                                     dep.disable_date DESC22                                     RANGE BETWEEN unbounded preceding AND 123                                     preceding) <= dep.disable_date /*+ 1*/ THEN24                            NULL25                           ELSE26                            dep.disable_date27                         END AS disable_date28                    FROM demo_eff_periods dep)29          UNPIVOT(new_date FOR date_pos IN(effectivity_date, disable_date)))30   GROUP BY component_item_id, dr;COMPONENT_ITEM_ID EFFECTIVITY_DATE DISABLE_DATE
----------------- ---------------- ------------1             1000         39991             4000         49992             1000         49993             1000         39994             1000         19994             2000         29994             3000         39995             1000         19995             4000         49999 rows selected

更多方法,欢迎大家讨论

相关文章:

  • 如何选择最佳路线?
  • sql盲注python脚本学习 (基于bWAPP靶场)
  • 谈谈hash算法
  • Leetcode-day31-01背包问题
  • 《Programming from the Ground Up》阅读笔记:p103-p116
  • Linux内核定时器
  • Java--Zuul网关中的过滤器
  • AIGC深度学习教程:Transformer模型中的Position Embedding实现与应用
  • IO与进程
  • 通信系统收发原理冷知识
  • Datawhale X 李宏毅苹果书 AI夏令营(深度学习入门)taks2
  • 跟《经济学人》学英文:2024年08月24日这期 What to make of America’s topsy-turvy economy
  • centos7安装Kafka单节点环境部署三-安装Logstash
  • MURF860AC-ASEMI智能AI专用MURF860AC
  • 虚幻游戏开发| 编辑器内正常运行但打包出错
  • IDEA 插件开发入门教程
  • java8-模拟hadoop
  • JavaScript DOM 10 - 滚动
  • linux学习笔记
  • Spring Cloud Feign的两种使用姿势
  • Vue.js源码(2):初探List Rendering
  • WebSocket使用
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 工程优化暨babel升级小记
  • 好的网址,关于.net 4.0 ,vs 2010
  • 缓存与缓冲
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 三分钟教你同步 Visual Studio Code 设置
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 译米田引理
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • scrapy中间件源码分析及常用中间件大全
  • ​水经微图Web1.5.0版即将上线
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #1015 : KMP算法
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (二)springcloud实战之config配置中心
  • (二十六)Java 数据结构
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (剑指Offer)面试题34:丑数
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • ***详解账号泄露:全球约1亿用户已泄露
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET轻量级ORM组件Dapper葵花宝典
  • @SuppressWarnings(unchecked)代码的作用
  • [20190113]四校联考
  • [AI aider] 打造终端AI搭档:Aider让编程更智能更有趣!