Oracle - 我想找出数据库表中的增量变化

Posted

技术标签:

【中文标题】Oracle - 我想找出数据库表中的增量变化【英文标题】:Oracle - I want to find out delta changes in a database table 【发布时间】:2020-01-31 04:04:16 【问题描述】:

该表每晚都会加载一些数据(大约 100K 行)。然后我的批处理作业从表中读取每条记录并进行处理。 我的要求是只处理那些在特定列中发生变化的记录,因为如果新记录与昨天的记录相比没有任何变化,则处理新记录是没有意义的(并且不必要地延迟了整个作业的处理)。

请假设以下表格结构

表名: STAGING_T

列名: PK_1 | COL1 | COL2 | COL3 | COL4 | CREATE_DATE | FLAG

CREATE_DATE 是数据加载到此表时的系统日期。这用于识别加载到此表中的每日数据。

    如果今天的数据中存在某行,但昨天(前一天)中没有,则将 FLAG 更新为“INSERT” 如果昨天的数据中存在某行但今天的数据中没有,请将 FLAG 更新为“DELETE” 如果今天和昨天的数据中都存在一行并且两条记录的 COL3 和 COL4 有任何变化,请将 FLAG 更新为“UPDATE”。 如果今天和昨天的数据中都存在一行并且两条记录的 COL3 和 COL4 都没有变化,请将 FLAG 更新为“NO CHANGE”。

我识别昨天和今天相同记录的常用列是 COL1 和 COL2。

**Sample Data**
PK_1    COL1    COL2    COL3    COL4    CREATE_DATE FLAG
1       1000    2000    a       x       31.01.2019  
2       1000    2001    b       y       31.01.2019  
3       1000    2002    c       z       31.01.2019  
4       1000    2000    aa      x       30.01.2019  
5       1000    2001    b       y       30.01.2019  
6       1000    2003    d       z       30.01.2019  


**Expected Output**

PK_1    COL1    COL2    COL3    COL4    CREATE_DATE FLAG
1       1000    2000    a       x       31.01.2019  UPDATE
2       1000    2001    b       y       31.01.2019  NO CHANGE
3       1000    2002    c       z       31.01.2019  INSERT
4       1000    2000    aa      x       30.01.2019  
5       1000    2001    b       y       30.01.2019  
6       1000    2003    d       z       30.01.2019  

任何帮助或建议都会很棒。 谢谢。

【问题讨论】:

您识别昨天和今天的相同记录的常用列有哪些?是 col1 和 col2 吗?示例数据和预期输出将帮助我们为您提供帮助。 是的,它是 col1 和 col2。我还将添加示例数据和预期输出。谢谢:) 我在问题中添加了示例数据和预期输出 【参考方案1】:

您可以先生成所需的值,然后使用合并语句。你可以试试下面 -

MERGE INTO DATA T 
USING (SELECT D.PK_1, D.COL1, D.COL2, D.COL3, CREATE_DATE, D.COL4,
       CASE WHEN CREATE_DATE = (SELECT MAX(CREATE_DATE) FROM DATA) THEN
            CASE WHEN LAG(COL3 || '`'||COL4) OVER(PARTITION BY COL1,COL2 ORDER BY CREATE_DATE) IS NULL
                      THEN 'INSERT'
                 WHEN LAG(COL3 || '`'||COL4) OVER(PARTITION BY COL1,COL2 ORDER BY CREATE_DATE)  = COL3 || '`'||COL4
                      THEN 'NO CHANGE'
                 ELSE 'UPDATE'
            END
       END FLAG
       FROM DATA D)
S ON (T.PK_1 = S.PK_1)
WHEN MATCHED THEN UPDATE SET T.FLAG = S.FLAG;

Here 是演示。

【讨论】:

【参考方案2】:

可能是这样的(未测试):

   update STAGING_T  set FLAG = 'INSERT' where PK_1  in (
   select PK_1 from 
   STAGING_T T1
   where  trunc(CREATE_DATE)=trunc(sysdate) --today
   and not exists (
   select 1  from  
   STAGING_T T2 
   where  trunc(CREATE_DATE)=trunc(sysdate-1) --yesterday
          and T1.COL1 = T2.COL1 and T1.COL2 = T2.COL2 and T1.COL3 = T2.COL3 
          and T1.COL4 = T2.COL4
   )

)


   update STAGING_T  set FLAG = 'DELETE' where PK_1  in (
   select PK_1 from 
   STAGING_T T1
   where  trunc(CREATE_DATE)=trunc(sysdate-1) --yesterday
   and not exists (
   select 1  from  
   STAGING_T T2 
   where  trunc(CREATE_DATE)=trunc(sysdate) --today
          and T1.COL1 = T2.COL1 and T1.COL2 = T2.COL2 and T1.COL3 = T2.COL3 
          and T1.COL4 = T2.COL4
   )

)

   update STAGING_T  set FLAG = 'update' where PK_1  in (
   select PK_1 from 
   STAGING_T T1
   where  trunc(CREATE_DATE)=trunc(sysdate-1) --yesterday
   and  exists (
   select 1  from  
   STAGING_T T2 
   where  trunc(CREATE_DATE)=trunc(sysdate) --today
          and T1.COL1 = T2.COL1 and T1.COL2 = T2.COL2 and T1.COL3 <> T2.COL3 
          and T1.COL4 <> T2.COL4
   )

)


   update STAGING_T  set FLAG = 'no change' where PK_1  in (
   select PK_1 from 
   STAGING_T T1
   where  trunc(CREATE_DATE)=trunc(sysdate-1) --yesterday
   and  exists (
   select 1  from  
   STAGING_T T2 
   where  trunc(CREATE_DATE)=trunc(sysdate) --today
          and T1.COL1 = T2.COL1 and T1.COL2 = T2.COL2 and T1.COL3 = T2.COL3 
          and T1.COL4 = T2.COL4
   )

)

【讨论】:

【参考方案3】:

您可以在源代码部分使用MERGE 语句和aggregate functions,如下所示:

MERGE INTO DATA T 
USING (
          SELECT COL1, COL2,
              MAX(CASE WHEN TRUNC(SYSDATE) = CREATE_DATE THEN PK_1 END) TODAY_PK,
              MAX(CASE WHEN TRUNC(SYSDATE) - 1 = CREATE_DATE  THEN PK_1 END) YEST_PK,
              COUNT(DISTINCT COL3) AS CNT_COL3,
              COUNT(DISTINCT COL4) AS CNT_COL4
          FROM DATA T
          WHERE CREATE_DATE BETWEEN TRUNC(SYSDATE - 1) AND TRUNC(SYSDATE)
          GROUP BY COL1, COL2
      )
S ON ( T.COL1 = S.COL1
       AND T.COL2 = S.COL2
       AND T.CREATE_DATE = TRUNC(SYSDATE) )
WHEN MATCHED THEN UPDATE SET T.FLAG = CASE
    WHEN S.YEST_PK IS NULL THEN 'INSERT'
    WHEN S.TODAY_PK IS NULL THEN 'DELETE'
    WHEN S.CNT_COL3 > 1
         OR S.CNT_COL4 > 1 THEN 'UPDATE'
    ELSE 'NO CHANGE'
END;

db<>fiddle demo

我假设您的 CREATE_DATE 是一个以时间为开始的日期,如果不是,请在查询中使用 TRUNC(CREATE_DATE) 而不是 CREATE_DATE。

干杯!!

【讨论】:

这没有产生正确的输出 - dbfiddle.uk/… 好的,有个小错误-1应该在MAX的左边。更新了答案。请立即检查!

以上是关于Oracle - 我想找出数据库表中的增量变化的主要内容,如果未能解决你的问题,请参考以下文章

怎么使用kettle进行增量数据的抽取

更新 hive 表中的增量记录

SQOOP增量抽取时,在HIVE中实现类似Oracle的merge操作

odi增量更新策略

java 如何通过接口把远程Oracle表中的数据同步到Mysql

oracle查询填补同一张表中缺失的数据