本篇紧跟第一篇, 和 第二篇无关
Oracle - 多区间按权重取值逻辑 ,分时区-多层级-取配置方案
Oracle - 多区间按权重取值逻辑 ,分时区-多层级-取配置方案(二)
先说需求:
某业务配置表,按配置的时间区间及组织层级取方案,形成报表展示出所有部门方案的取值;
例如,总公司配置20230101-20231231为方案3, 分公司配置 20230301-20230731 为方案2,部门配置20230601-20231031 为方案1,配置优先级为 部门> 分公司>总公司 ,即啥都没配则使用总公司默认值;
第一篇说到,拆分出了很多日期边界,由于时间区间边界当天,不好判断,只能单独拉出来作为一条记录, 虽然保证了边界数据的准确性,但拆分太散后 无法合并到一起。
最近一直在琢磨,琢磨不同方案 ,各种方法,在刚刚发完第二篇的适合,灵光一现,一下解决了。
还是得说明下, 这里是默认 数据源是正确,同一优先级的时间区间不重复的(相连的倒是没关系)
数据源:
最后输出结果:
按优先级拆分时间区间。
前前后后零零散散的时间处理的,就加了很多中间表, 不过目前效果已经达到了,
后面就进行相关代码优化就好了。
代码:
WITH A AS --基础数据(SELECT 3 AS LEVE, '20230101' AS BEGINDATE, '20231231' AS ENDDATEFROM DUALUNION ALLSELECT 2 AS LEVE, '20230301' AS BEGINDATE, '20230731' AS ENDDATEFROM DUALUNION ALLSELECT 2 AS LEVE, '20231001' AS BEGINDATE, '20231130' AS ENDDATEFROM DUALUNION ALLSELECT 1 AS LEVE, '20230201' AS BEGINDATE, '20230831' AS ENDDATEFROM DUAL)
--SELECT * FROM A ORDER BY A.BEGINDATE
, C AS --生产日期节点前后一天,将时间边界当天天单独作为一条记录(SELECT DAY,TO_CHAR(TO_DATE(DAY, 'yyyyMMdd') - 1, 'yyyyMMdd') AS DAY_LAST,TO_CHAR(TO_DATE(DAY, 'yyyyMMdd') + 1, 'yyyyMMdd') AS DAY_NEXT,ROWNUM NFROM (SELECT BEGINDATE AS DAY FROM A UNION SELECT ENDDATE FROM A ORDER BY DAY)),
--最后进行查询匹配, 权重取最小
E AS(SELECT D.*, MIN(A.LEVE) AS LEVEFROM (SELECT C1.DAY_NEXT AS BEGINDATE, C2.DAY_LAST AS ENDDATEFROM C C1, C C2WHERE C1.N + 1 = C2.NUNIONSELECT DAY AS BEGINDATE, DAY AS ENDDATEFROM CORDER BY BEGINDATE) D --生成最小的时间区间,并关联各个时间边界当天LEFT JOIN AON D.BEGINDATE >= A. BEGINDATEAND D.ENDDATE <= A.ENDDATEGROUP BY D.BEGINDATE, D.ENDDATEORDER BY D.BEGINDATE, D.ENDDATE)
--分段进行汇总
,
F AS(SELECT NVL(C.DAY, E.BEGINDATE) AS BEGINDATE, NVL(C2.DAY, E.ENDDATE) AS ENDDATE, E.LEVEFROM ELEFT JOIN CON E.BEGINDATE <> E.ENDDATEAND E.BEGINDATE = C.DAY_NEXTAND EXISTS (SELECT 1FROM E E2WHERE E2.BEGINDATE = E2.ENDDATEAND E2.BEGINDATE = C.DAYAND E.LEVE = E2.LEVE)LEFT JOIN C C2ON E.BEGINDATE <> E.ENDDATEAND E.ENDDATE = C2.DAY_LASTAND EXISTS (SELECT 1FROM E E2WHERE E2.BEGINDATE = E2.ENDDATEAND E2.ENDDATE = C2.DAYAND E.LEVE = E2.LEVE)ORDER BY E.LEVE, E.BEGINDATE)
--去掉边界日期
,
G AS(SELECT F.*, ROWNUM NFROM FWHERE F.BEGINDATE <> F.ENDDATEOR NOT EXISTS(SELECT 1FROM F F2WHERE F2.BEGINDATE = F2.ENDDATEAND F2.LEVE = F.LEVEAND (F2.BEGINDATE = F. BEGINDATE OR F2.BEGINDATE = F. ENDDATE))ORDER BY BEGINDATE),
H(B1,
E1,
L1) AS(SELECT BEGINDATE, ENDDATE, LEVEFROM GWHERE G.BEGINDATE NOT IN (SELECT ENDDATE FROM G)UNION ALLSELECT H.B1, H2.ENDDATE AS E1, H.L1FROM HJOIN G H2ON H.E1 = H2.BEGINDATEAND H.L1 = H2.LEVE)
SELECT B1, MAX(E1), L1 FROM H GROUP BY B1, L1;