我正在尝试编写一个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上添加索引。如果指数没有帮助,那么对于糖蜜缓慢方面有什么建议吗?
答案 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;
通过这种方式,您可以更新与旧值相关的注册表,并使用新值插入新行。
我希望这可以帮助您解决问题。