Sql Oracle-与插入、更新和删除合并的过程

Sql Oracle-与插入、更新和删除合并的过程,sql,oracle,stored-procedures,oracle19c,Sql,Oracle,Stored Procedures,Oracle19c,我需要创建一个程序,以尽可能有效的方式处理这个案例,因为数据量非常大 我有一个名为ORDER_a的表,它每天都会收到一个完整的负载,并且所有的记录都会被再次插入。 我有一个名为ORDER_B的表,它是ORDER_a的副本,包含相同的数据和一些额外的控制日期。 我还有一个表管理器来保存开始和完成日期,以及过程是否正在运行 在所有插入都按顺序A完成之后,我想执行一个过程,对于顺序A上的每个记录,必须查找具有相同标识符主键的记录:表B中的顺序id 如果存在具有相同order_id的记录,并且任何其他列

我需要创建一个程序,以尽可能有效的方式处理这个案例,因为数据量非常大

我有一个名为ORDER_a的表,它每天都会收到一个完整的负载,并且所有的记录都会被再次插入。 我有一个名为ORDER_B的表,它是ORDER_a的副本,包含相同的数据和一些额外的控制日期。 我还有一个表管理器来保存开始和完成日期,以及过程是否正在运行

在所有插入都按顺序A完成之后,我想执行一个过程,对于顺序A上的每个记录,必须查找具有相同标识符主键的记录:表B中的顺序id

如果存在具有相同order_id的记录,并且任何其他列都已更改,则必须对表B执行更新 如果存在具有相同order_id且没有值的记录​​在已修改的其他列中,不应执行任何操作,表B中的记录必须保持不变。 如果没有具有相同订单id的记录,则必须将其插入表B中。 如果订单_B上有一条记录在订单_a上不再存在,并且该记录已被删除,则列标志_deleted必须更新为1。 我的桌子是这样的

CREATE TABLE ORDER_A
(
    ORDER_ID NUMBER NOT NULL,
    ORDER_CODE VARCHAR2(50),
    ORDER_STATUS VARCHAR2(20),
    ORDER_USER_ID NUMBER,
    ORDER_DATE TIMESTAMP(6),
    CHECKSUM_CODE VARCHAR2(40),
    PRIMARY KEY (ORDER_ID)
);

CREATE TABLE ORDER_B
(
    ORDER_ID NUMBER NOT NULL,
    ORDER_CODE VARCHAR2(50),
    ORDER_STATUS VARCHAR2(20),
    ORDER_USER_ID NUMBER,
    ORDER_DATE TIMESTAMP(6)
    INSERT_AT TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP,
    UPDATED_AT TIMESTAMP(6),
    CHECKSUM_CODE VARCHAR2(40),
    FLAG_DELETED NUMBER(1),
    PRIMARY KEY (ORDER_ID)
);

-- index on checksum column for both tables
CREATE INDEX idx_cksum on ORDER_A (CHECKSUM_CODE ASC);
CREATE INDEX idx_cksum on ORDER_B (CHECKSUM_CODE ASC);


-- Manager table
CREATE TABLE MANAGER
(
    TABLE_NAME VARCHAR2(40),
    PROCEDURE_NAME VARCHAR2(50),
    START_TS TIMESTAMP(6),
    FINISH_TS TIMESTAMP(6),
    IS_RUNNING NUMBER(1)
);
    

我在考虑下面的过程,但我不确定这是否是最好的方法以及如何处理删除案例

create or replace procedure MERGE_DATA_ORDER
DECLARE
 is_running number;
 ex_running EXCEPTION;
BEGIN

SELECT IS_RUNNING INTO is_running FROM MANAGER WHERE PROCEDURE_NAME = 'MERGE_DATA_ORDER';

IF is_running = 1 
   then RAISE ex_running

ELSE

-- Update the flag on manager table
UPDATE MANAGER SET IS_RUNNING = 1, START_TS = SYSTIMESTAMP WHERE PROCEDURE_NAME = 'MERGE_DATA_ORDER';
COMMIT;
        

-- update all records with a checksum using STANDARD_HASH with MD5
    UPDATE ORDER_A
        SET CHECKSUM_CODE =
            STANDARD_HASH
            (
                ORDER_ID ||
                ORDER_CODE ||
                ORDER_STATUS ||
                ORDER_USER_ID ||
                ORDER_DATE,
                'MD5'
            );          
        COMMIT;
        
-- then, I do a MERGE operation, using the checksum as a comparator 
 merge into ORDER_B b
    using (select a.* from  ORDER_A a) m
        on (m.ORDER_ID = b.ORDER_ID)
    when matched then
      update
        set 
            b.ORDER_ID = m.ORDER_ID,
            b.ORDER_CODE = m.ORDER_CODE,
            b.ORDER_STATUS = m.ORDER_STATUS,
            b.ORDER_USER_ID = m.ORDER_USER_ID,
            b.ORDER_DATE = m.ORDER_DATE,            
            b.COD_CHECKSUM = m.COD_CHECKSUM,
            b.DAT_UPDATE = SYSTIMESTAMP
      where b.CHECKSUM_CODE <> m.CHECKSUM_CODE

    when not matched then
      insert (
            b.ORDER_ID,
            b.ORDER_CODE,
            b.ORDER_STATUS,
            b.ORDER_USER_ID,
            b.ORDER_DATE,
            b.COD_CHECKSUM
            )
        values (
            m.ORDER_ID,
            m.ORDER_CODE,
            m.ORDER_STATUS,
            m.ORDER_USER_ID,
            m.ORDER_DATE,
            m.COD_CHECKSUM
            );

   END IF;

-- set the flag to 0   
   UPDATE MANAGER SET IS_RUNNING = 0, FINISH_TS = SYSTIMESTAMP WHERE PROCEDURE_NAME = 'MERGE_DATA_ORDER';
   COMMIT;
END;
/

我需要一些帮助来完成这段代码,性能提示和处理删除问题

我认为您可以将此作为一条语句作为数据加载的一部分来完成。假设订单A已经加载,但我稍后会对此进行评论。然后,您可以通过在ORDER_a和ORDER_B之间进行完全外部联接,以及使用用例语句从ORDER_a或ORDER_B投影正确的值来定义插入/更新的结果。类似地,您可以投影DELTED标志。它看起来像这样。在本例中,我跳过了MD5,但如果真的需要,可以添加它-稍后也会详细介绍

select
       case 
       when ( b.order_id is null ) then a.order_id
       else case when (
            b.ORDER_ID      != m.ORDER_ID or
            b.ORDER_CODE    != m.ORDER_CODE or
            b.ORDER_STATUS  != m.ORDER_STATUS or
            b.ORDER_USER_ID != m.ORDER_USER_ID or
            b.ORDER_DATE    != m.ORDER_DATE     or    
            b.DAT_UPDATE    != SYSTIMESTAMP ) then b.order_id else a.order_id end
        end as newOrder_id
     , case when ( b.order_id is null ) then a.order_code
       else case when (
            b.ORDER_ID      != m.ORDER_ID or
            b.ORDER_CODE    != m.ORDER_CODE or
            b.ORDER_STATUS  != m.ORDER_STATUS or
            b.ORDER_USER_ID != m.ORDER_USER_ID or
            b.ORDER_DATE    != m.ORDER_DATE     or    
            b.DAT_UPDATE    != SYSTIMESTAMP ) then b.order_code else a.order_code end
        end as newOrder_code 
     , case when ( b.order_id is null ) then a.order_status
       else case when (
            b.ORDER_ID      != m.ORDER_ID or
            b.ORDER_CODE    != m.ORDER_CODE or
            b.ORDER_STATUS  != m.ORDER_STATUS or
            b.ORDER_USER_ID != m.ORDER_USER_ID or
            b.ORDER_DATE    != m.ORDER_DATE     or    
            b.DAT_UPDATE    != SYSTIMESTAMP ) then b.order_status else a.order_status end
        end as newOrder_status
/* etc... ( Repeat for all projected columns ) 
   Then for the flag_deleted column */
     , case when ( a.order_id is null ) then 1
            when ( b.order_id is null ) then 0
            else b.flag_deleted 
       end as newFlag_deleted
from Order_b b
full outer join Order_a a
on b.order_id = a.order_id
ORDER_A可能是一个外部表,所以您只需要在它前面加上

CREATE TABLE NEW_ORDER_A as select....
然后你就有了你需要的结果

在您的示例中,您的性能下降的地方是ORDER_a的更新。您正在生成redo、undo并失去任何压缩优势。您也在维护索引,但不需要索引。 假设您有资源,您现在可以使用直接路径和并行,这将可以很好地扩展

最后,如果您真的需要MD5,则需要在每列之间添加一个特殊字符,否则is将不明确。例如,以下文件具有相同的MD5

COL1   COL2
AA     BBB
AAB    BB

删除将需要一个单独的命令;没有办法使合并的一部分。类似于从订单b中删除,其中订单id不在订单a中选择订单id;此外,插入时,请确保将校验和代码从订单A带到订单B。