ORACLE如果任何字段发生更改,请添加新记录

时间:2018-10-10 18:06:48

标签: sql oracle oracle11g

我正在尝试编写一个Oracle过程。我有一个表,当前正在使用合并语句。更改记录后,它会对其进行更新;如果是新记录,则会将其添加。

但是,我们要跟踪更改的记录。因此,我添加了三个字段:startdate,enddate,currentflag。如果有任何更改,我不想更新记录,我想添加一个新记录。但是我确实想添加结束日期并更改旧记录上的标志。

所以,如果我有一个这样的表:

TableID
Field1
Field2
Field3
StartDate
EndDate
CurrentFlag

它具有这样的数据

TableID   Field1   Field2   Field3  StartDate   EndDate   CurrentFlag
001       DataA    Cow      Brown   3-Oct-18              Y
001       DataA    Cow      White   1-Sep-18    3-Oct-18  N
002       DataB    Horse    Dapple  3-Oct-18              Y

我想合并一些数据

TableID   Field1   Field2   Field3
001       NewData  Cow      Black 
002       DataB    Horse    Dapple
005       Data3    Cat      Black

这样决赛桌看起来像这样

TableID   Field1   Field2   Field3  StartDate   EndDate   CurrentFlag
001       DataA    Cow      Brown   3-Oct-18    10-Oct-18 N
001       DataA    Cow      White   1-Sep-18    3-Oct-18  N
001       NewData  Cow      Black   10-Oct-18             Y
002       DataB    Horse    Dapple  3-Oct-18              Y
005       Data3    Cat      Black   10-Oct-18             Y

我的伪代码是

for each record in source file
   find current record in dest table (on ID and flag = Y)
   if any other fields do not match (Field1, Field2, Field3)
    then update current record, set enddate, current flag to n
        and add new record with startdate = sysdate, current flag is Y
   if no match found, then add new record with startdate = sysdate, current flag is Y

我不确定如何将伪代码转换为Oracle SQL代码。我可以使用相同的MERGE语句,但在WHEN MATCHED中添加检查以查看其他任何字段是否不同吗?

我将针对几个表执行此操作,其中一些表具有大量记录和多个字段。因此,我需要找出一些可行且不如糖蜜那么慢的东西。

更新 我已经按照建议创建了一个过程,并对其进行了一些修改,所以它可以正常工作:

CREATE OR REPLACE PROCEDURE TESTPROC AS

BEGIN
DECLARE 
l_count NUMBER;
CURSOR TRN is
    SELECT * from sourceTable;

BEGIN 
    FOR each_record IN TRN
    LOOP
        -- if a record found but fields differ ...
        l_count := 0;
        SELECT COUNT(*) INTO l_count 
        FROM destTable DIM
        WHERE each_record.TableID = DIM.TableID
          and (each_record.Field1 <> DIM.Field1
          or each_record.Field2 <> DIM.Field2
          or each_record.Field13 <> DIM.Field3)
          AND DIM.CurrentFlag = 'Y';

        -- ... then update existing current record, and add with new data
        IF l_count > 0 THEN
            UPDATE destTable DIM
              SET EndDate = sysdate
                 ,CurrentFlag = 'N'
               WHERE each_record.TableID = DIM.TableID;

            INSERT INTO destTable 
                     (TableID
                    , Field1
                    , Field2
                    , Field3
                    , StartDate
                    , CurrentFlag)
            VALUES (each_record.TableID
                    , each_record.Field1
                    , each_record.Field2
                    , each_record.Field3
                    , sysdate
                    , 'Y');
          COMMIT;
        END IF;

        -- if no record found with this key...
        l_count := 0;
        SELECT COUNT(*) INTO l_count 
        FROM destTable DIM
        WHERE each_record.TableID = DIM.TableID;

        -- then add a new record
        IF l_count = 0 THEN
            INSERT INTO destTable 
                     (TableID
                    , Field1
                    , Field2
                    , Field3
                    , StartDate
                    , CurrentFlag)
            VALUES (each_record.TableID
                    , each_record.Field1
                    , each_record.Field2
                    , each_record.Field3
                    , sysdate
                    , 'Y');
        END IF;
    END LOOP;    
    COMMIT;
END;
END TESTPROC 

在我的小桌子上,效果很好。现在,我正在一个较大的表(800k条记录,但绝不是最大的表)上尝试此操作,并且在运行时更新了此问题。已经快一个小时了,显然这是不能接受的。程序返回后,我将在TableID,TableID和CurrentFlag上添加索引。如果指数没有帮助,那么对于糖蜜缓慢方面有什么建议吗?

2 个答案:

答案 0 :(得分:0)

您可以为此编写一个简单过程:

DECLARE

l_count NUMBER;
CURSOR C1 is
-- YOUR DATA FROM SOURCE 

BEGIN

 for each_record in c1
 l_count := 0;
   SELECT COUNT(*) into l_count from destination_table where field1= 
  eachrecord.field1 and .... and flag = 'Y'; -- find current record in dest table (on ID and flag = Y)

--  if any other fields do not match (Field1, Field2, Field3)
IF L_COUNT > 0 THEN 
   update current record, set enddate, current flag to n
END IF;
    INSERT new record with startdate = sysdate, current flag is Y
END;

由OP修改:这导致了正确的方向。下面的代码将达到目的,只要在TableID和(TableID,CurrentFlag)上也有索引。

CREATE OR REPLACE PROCEDURE TESTPROC AS

BEGIN
DECLARE 
l_count NUMBER;
CURSOR TRN is
    SELECT * from sourceTable;

BEGIN 
    FOR each_record IN TRN
    LOOP
        -- if a record found but fields differ ...
        l_count := 0;
        SELECT COUNT(*) INTO l_count 
        FROM destTable DIM
        WHERE each_record.TableID = DIM.TableID
          and (each_record.Field1 <> DIM.Field1
          or each_record.Field2 <> DIM.Field2
          or each_record.Field13 <> DIM.Field3)
          AND DIM.CurrentFlag = 'Y';

        -- ... then update existing current record, and add with new data
        IF l_count > 0 THEN
            UPDATE destTable DIM
              SET EndDate = sysdate
                 ,CurrentFlag = 'N'
               WHERE each_record.TableID = DIM.TableID;

            INSERT INTO destTable 
                     (TableID
                    , Field1
                    , Field2
                    , Field3
                    , StartDate
                    , CurrentFlag)
            VALUES (each_record.TableID
                    , each_record.Field1
                    , each_record.Field2
                    , each_record.Field3
                    , sysdate
                    , 'Y');
          COMMIT;
        END IF;

        -- if no record found with this key...
        l_count := 0;
        SELECT COUNT(*) INTO l_count 
        FROM destTable DIM
        WHERE each_record.TableID = DIM.TableID;

        -- then add a new record
        IF l_count = 0 THEN
            INSERT INTO destTable 
                     (TableID
                    , Field1
                    , Field2
                    , Field3
                    , StartDate
                    , CurrentFlag)
            VALUES (each_record.TableID
                    , each_record.Field1
                    , each_record.Field2
                    , each_record.Field3
                    , sysdate
                    , 'Y');
        END IF;
    END LOOP;    
    COMMIT;
END;
END TESTPROC 

答案 1 :(得分:0)

也许您可以使用触发器来执行该操作。

CREATE OR REPLACE TRIGGER insTableID
BEFORE INSERT OR UPDATE
ON tableID
FOR EACH ROW
DECLARE
    v_exists NUMBER := -1;
BEGIN
    SELECT COUNT(1) INTO v_exists FROM tableID t where t.Field1 = :new.Field1 and ... ;
    IF INSERTING THEN
        IF v_exist > 0 THEN
            null;--your DML update statement
        ELSE
            null;--your DML insert statement
        END;
    END IF;
    IF UPDATING THEN
        null;--your DML statement for update the old registry and a DML for insert the new registry.
    END IF;
END;

通过这种方式,您可以更新与旧值相关的注册表,并使用新值插入新行。

我希望这可以帮助您解决问题。