减少Oracle中的重叠日期范围

wqlqzqxt  于 2023-05-06  发布在  Oracle
关注(0)|答案(2)|浏览(118)

我在创建Oracle SQL语句时遇到了一个两难的问题,需要满足以下要求:
假设我在PROD_TABLE中有这些记录。

*PROD_TABLE

分配ID生效开始日期有效结束日期行动原因
一千零一2010年1月1日2015年2月2日名称变更
一千零一2015年3月2日2021年5月5日名称变更
一千零一2021年5月6日2023年3月3日名称变更
一千零一2023年3月4日12/31/4712名称变更

假设我在STAGING_TABLE中有这些记录。

STAGING_TABLE

分配ID生效开始日期有效结束日期行动原因
一千零一2015年3月2日2021年5月5日部门变更
一千零一2021年5月6日12/31/4712经理变更

**逻辑将如下...**这些记录将保持不变。仅ACTION_REASON字段将被更新(基于STAGING_TABLE.ACTION_REASON字段的更新)。

分配ID生效开始日期有效结束日期行动原因
一千零一2010年1月1日2015年2月2日名称变更
一千零一2015年3月2日2021年5月5日部门变更

将删除PROD_TABLE中的这些记录,因为日期范围在STAGING_TABLE中重叠。
| 分配ID|生效开始日期|有效结束日期|行动原因|
| --------------|--------------|--------------|--------------|
| 一千零一|2021年5月6日|2023年3月3日|名称变更|
| 一千零一|2023年3月4日|12/31/4712|名称变更|
重叠的日期范围将根据STAGING_TABLE中的日期范围进行更改。
| 分配ID|生效开始日期|有效结束日期|行动原因|
| --------------|--------------|--------------|--------------|
| 一千零一|2021年5月6日|12/31/4712|经理变更|
因此,预期输出将是:预期产出
| 分配ID|生效开始日期|有效结束日期|行动原因|
| --------------|--------------|--------------|--------------|
| 一千零一|2010年1月1日|2015年2月2日|名称变更|
| 一千零一|2015年3月2日|2021年5月5日|部门变更|
| 一千零一|2021年5月6日|12/31/4712|经理变更|

CREATE TABLE PROD_TABLE(Assignment_ID   number, Effective_START_Date date,  Effective_END_Date date, ACTION_REASON VARCHAR2(100));

INSERT INTO PROD_TABLE VALUES (30001,   TO_DATE('1/1/2010', 'MM/DD/YYYY'),  TO_DATE('2/2/2015', 'MM/DD/YYYY'), 'Name Change');
INSERT INTO PROD_TABLE VALUES (30001,   TO_DATE('2/3/2015', 'MM/DD/YYYY'),  TO_DATE('5/5/2021', 'MM/DD/YYYY'), 'Name Change');
INSERT INTO PROD_TABLE VALUES (30001,   TO_DATE('5/6/2021', 'MM/DD/YYYY'),  TO_DATE('3/3/2023', 'MM/DD/YYYY'), 'Name Change');
INSERT INTO PROD_TABLE VALUES (30001,   TO_DATE('3/4/2023', 'MM/DD/YYYY'),  TO_DATE('12/31/4712', 'MM/DD/YYYY'), 'Name Change');

CREATE TABLE STAGING_TABLE(Assignment_ID    number, Effective_START_Date date,  Effective_END_Date date, ACTION_REASON VARCHAR2(100));

INSERT INTO STAGING_TABLE VALUES (30001,    TO_DATE('5/6/2021', 'MM/DD/YYYY'),  TO_DATE('12/31/4712', 'MM/DD/YYYY'), 'Manager Change');

现在我有这个SQL无法拉取正确的结果:(

WITH query1 AS (
    SELECT staging_table.assignment_id,
           GREATEST(staging_table.effective_start_date, prod_table.effective_start_date) AS effective_START_date,
           LEAST(staging_table.effective_end_date, prod_table.effective_end_date) AS effective_END_date,
           staging_table.ACTION_REASON
      FROM PROD_TABLE prod_table
      JOIN STAGING_TABLE staging_table
        ON prod_table.Assignment_ID = staging_table.Assignment_ID
     WHERE prod_table.effective_start_date < staging_table.effective_end_date
       AND prod_table.effective_end_date > staging_table.effective_start_date
),  
query2 AS (   
    SELECT * 
      FROM query1
     UNION
    SELECT prod_table.Assignment_ID,
           prod_table.effective_start_date,
           prod_table.effective_end_date,
           prod_table.ACTION_REASON
      FROM STAGING_TABLE prod_table
     WHERE NOT EXISTS (SELECT 1 
                         FROM query1 qry
                        WHERE prod_table.Assignment_ID = qry.Assignment_ID
                          AND prod_table.effective_start_date BETWEEN qry.effective_start_date AND qry.effective_end_date)
)
SELECT qry.* 
  FROM query2 qry;
y53ybaqx

y53ybaqx1#

这是我现在唯一想要的场景
对于您显示的具有精确日期匹配的数据,您可以使用外部联接和合并来在存在匹配时使用分段值,并使用distinct来删除重复项,然后为第二个分段值引入重复项:

select distinct pt.assignment_id,
  coalesce(st.effective_start_date, pt.effective_start_date) as effective_start_date,
  coalesce(st.effective_end_date, pt.effective_end_date) as effective_end_date,
  coalesce(st.action_reason, pt.action_reason) as action_reason
from prod_table pt
left join staging_table st
on st.assignment_id = pt.assignment_id
and (st.effective_start_date = pt.effective_start_date
  or st.effective_end_date = pt.effective_end_date)
order by assignment_id, effective_start_date
分配ID生效日期生效结束日期行动_原因
300012010-01-012015-02-02 2015-02-02名称变更
300012015-02-03 2015-02-03二〇二一年五月五日部门变更
300012021-05-06 2021-05-064712-12-31经理变更

如果你想更新主表中的数据,那么你可以使用merge,使用基本相同的逻辑,并使用delete where子句来删除现在重复的数据:

merge into prod_table pt
using staging_table st
on (
  st.assignment_id = pt.assignment_id
  and (st.effective_start_date = pt.effective_start_date
    or st.effective_end_date = pt.effective_end_date)
)
when matched then update set
  pt.effective_end_date = st.effective_end_date,
  pt.action_reason = st.action_reason
delete where pt.effective_start_date != st.effective_start_date

它更新所有匹配的行,但只更新结束日期和原因,而保留原始的开始日期;然后删除那些与暂存开始日期不匹配的匹配行,只留下一行。

select * from prod_table
分配ID生效日期生效结束日期行动_原因
300012010-01-012015-02-02 2015-02-02名称变更
300012015-02-03 2015-02-03二〇二一年五月五日部门变更
300012021-05-06 2021-05-064712-12-31经理变更

fiddle
但是如果你有与现有日期不完全匹配的分期数据,那么它可能会变得更加复杂;如果是这样的话,那么你需要问一个新的问题,清楚地列出你需要处理的所有场景(间隙,重叠,冲突,...)以及你对每一个场景的预期行为。

3npbholx

3npbholx2#

虽然我不太确定你到底想得到什么(条件是什么),但在我看来,这似乎可以用MODEL clause来完成,它允许你像用excel工作表一样管理数据。
下面的代码并没有完成这项工作,但向您展示了一些调节和操作数据的方法:
样本数据:

--  stg table
        ID EFF_START EFF_END   REASON            
---------- --------- --------- ------------------
     30001 06-MAY-21 31-DEC-12 Manager Change    -- end year 4712
     
-- prod table
        ID EFF_START EFF_END   REASON                                                                                             
---------- --------- --------- ------------------
     30001 01-JAN-10 02-FEB-15 Name Change       
     30001 03-FEB-15 05-MAY-21 Name Change       
     30001 06-MAY-21 03-MAR-23 Name Change       
     30001 04-MAR-23 31-DEC-12 Name Change      -- end year 4712

主SQL:

Select   ID, S_EFF_START, S_EFF_END, S_REASON, RN, EFF_START, EFF_END, DATSTAT, REASON, RSNSTAT
From
    ( Select  ROW_NUMBER() OVER(Partition By p.ID Order By p.ID, p.EFF_START) "RN",
              p.ID "ID", p.EFF_START "EFF_START", p.EFF_END "EFF_END", p.REASON "REASON",
              s.EFF_START "S_EFF_START", s.EFF_END "S_EFF_END", s.REASON "S_REASON"
      From stg s
      Left Join prod p ON( 1 = 1 ) 
    )
    MODEL Partition By (ID)
          Dimension By (RN, S_EFF_START, S_EFF_END)
          Measures (EFF_START, EFF_END, REASON, S_REASON, Cast('' As VarChar2(64)) "DATSTAT", Cast('' As VarChar2(64)) "RSNSTAT")
    Rules (
              EFF_START[ANY, ANY, ANY] =  CASE  WHEN  EFF_START[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) 
                                                THEN  CV(S_EFF_START) 
                                          ELSE EFF_START[CV(), CV(), CV()]
                                          END,
              EFF_END[ANY, ANY, ANY]   =  CASE  WHEN  EFF_END[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) 
                                                THEN  CV(S_EFF_END) 
                                          ELSE  EFF_END[CV(), CV(), CV()]
                                          END,
              REASON[ANY, ANY, ANY]    =  CASE  WHEN  EFF_START[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) And 
                                                      EFF_END[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) 
                                                THEN  S_REASON[CV(), CV(), CV()] 
                                          ELSE  REASON[CV(), CV(), CV()]
                                          END,
              --
              DATSTAT[ANY, ANY, ANY]   =  CASE  WHEN   EFF_START[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) OR
                                                      EFF_END[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) 
                                                THEN  'Date(s) changed to one(s) from stg table' 
                                          ELSE 'No change'
                                          END,
              RSNSTAT[ANY, ANY, ANY]  =  CASE  WHEN   EFF_START[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) OR
                                                      EFF_END[CV(), CV(), CV()] Between CV(S_EFF_START) And CV(S_EFF_END) 
                                                THEN  'REASON changed' 
                                          ELSE 'No change'
                                          END
          )

结果:

--  
--      R e s u l t : 
        ID S_EFF_START S_EFF_END S_REASON                       RN EFF_START EFF_END   DATSTAT                                     REASON                 RSNSTAT                
---------- ----------- --------- ---------------------- ---------- --------- --------- ------------------------------------------- ---------------------- -----------------------
     30001 06-MAY-21   31-DEC-12 Manager Change                  1 01-JAN-10 02-FEB-15 No change                                   Name Change            No change              
     30001 06-MAY-21   31-DEC-12 Manager Change                  2 03-FEB-15 05-MAY-21 No change                                   Name Change            No change              
     30001 06-MAY-21   31-DEC-12 Manager Change                  3 06-MAY-21 31-DEC-12 Date(s) changed to one(s) from stg table    Manager Change         REASON changed         
     30001 06-MAY-21   31-DEC-12 Manager Change                  4 06-MAY-21 31-DEC-12 Date(s) changed to one(s) from stg table    Manager Change         REASON changed

它看起来有点复杂,但它是可靠和快速的。如果你能看一下文档-它迟早会派上用场的。

相关问题