我有几个Oracle自定义数据类型(CDT),其中一个是存储过程的输入参数。
CREATE OR REPLACE TYPE ENTITY_BR AS OBJECT(
ID NUMBER
);
CREATE OR REPLACE TYPE ENTITY_BR_LIST AS TABLE OF ENTITY_BR;
CREATE OR REPLACE TYPE ENTITY AS OBJECT(
ID_C VARCHAR2(25 BYTE),
ID_I NUMBER,
NAME_C VARCHAR2(100 BYTE),
CODE_I NUMBER,
DESC_C VARCHAR2(1024 BYTE),
BR ENTITY_BR_LIST
);
CREATE OR REPLACE TYPE ENTITY_LIST AS TABLE OF ENTITY;
CREATE OR REPLACE TYPE CRDS AS OBJECT(
ID_I NUMBER,
VERSION_I NUMBER,
STATUS_C VARCHAR2(20 BYTE),
ENTITY ENTITY_LIST
);
CREATE OR REPLACE TYPE LIST AS TABLE OF CRDS;
现在我有如下存储过程。输入是CRDSLIST,它一次作为一个包含5000个项目的数组传递。这个过程需要花费很多时间来执行,并且每次执行都会增加时间。不知道会出现什么问题。
CREATE OR REPLACE PACKAGE LOAD AS
PROCEDURE INSERT_DATA (crds CRDSLIST);
END;
CREATE OR REPLACE PACKAGE BODY LOAD AS
PROCEDURE INSERT_DATA (crds IN CRDSLIST)
AS
SID_I_NEW NUMBER;
ENTITY_SID_I_NEW NUMBER;
LAST_DT TIMESTAMP;
ELAPSED_TIME NUMBER;
BEGIN
FOR i IN 1 .. crds.COUNT
LOOP
BEGIN
SELECT MAX(LAST_UPDATED_DT) INTO LAST_DT
FROM T_REF1
GROUP BY ID_I
HAVING ID_I = crds(i).ID_I;
EXCEPTION
WHEN NO_DATA_FOUND THEN LAST_DT := NULL;
END;
IF LAST_DT IS NOT NULL THEN
SELECT (EXTRACT(DAY FROM SYSDATE - LAST_DT) *24 * 60 * 60) +
(EXTRACT(HOUR FROM SYSDATE - LAST_DT) * 60 * 60) +
(EXTRACT(MINUTE FROM SYSDATE - LAST_DT) * 60) +
EXTRACT(SECOND FROM SYSDATE - LAST_DT)
INTO ELAPSED_TIME
FROM dual;
END IF;
IF LAST_DT IS NULL OR ELAPSED_TIME >= 60 THEN
BEGIN
INSERT INTO T_REF1(SID_I, ID_I, VERSION_I, ENTITY_IND_STATUS_C, LAST_UPDATED_DT)
VALUES(SEQ1_SID.NEXTVAL, crds(i).ID_I, crds(i).VERSION_I, crds(i).ENTITY_IND_STATUS_C, SYSDATE) RETURNING SID_I INTO SID_I_NEW;
FOR j IN 1 .. crds(i).ENTITY.COUNT
LOOP
INSERT INTO T_REF2(SID_I, UCM_SID_I, ID_C, ID_I, NAME_C) VALUES(SEQ2_SID.NEXTVAL, SID_I_NEW, crds(i).ENTITY(j).ID_C, crds(i).ENTITY(j).ID_I, crds(i).ENTITY(j).NAME_C) RETURNING SID_I INTO ENTITY_SID_I_NEW;
IF crds(i).ENTITY(j).BR IS NOT NULL THEN
FOR k IN 1 .. crds(i).ENTITY(j).BR.COUNT
LOOP
INSERT INTO T_REF3(SID_I, ENTITY_SID_I, BR_ID, VERSION_I, LAST_UPDATED_DT) VALUES(SEQ3_SID.NEXTVAL, ENTITY_SID_I_NEW, crds(i).ENTITY(j).BR(k).ID_I, crds(i).VERSION_I, SYSDATE);
END LOOP;
END IF;
IF (crds(i).ENTITY(j).DESC_C IS NOT NULL) THEN
INSERT INTO T_REF2_VALIDATION(SID_I, ENTITY_SID_I, ERROR_CODE_I, ERROR_DESC_C, LAST_UPDATED_DT) VALUES(SEQ4_SID.NEXTVAL, ENTITY_SID_I_NEW, crds(i).ENTITY(j).CODE_I, crds(i).ENTITY(j).DESC_C, SYSDATE);
END IF;
END LOOP;
IF (crds(i).ERROR_DESC_C IS NOT NULL) THEN
INSERT INTO T_REF1_VALIDATION(SID_I, UCM_SID_I, ERROR_CODE_I, ERROR_DESC_C, LAST_UPDATED_DT) VALUES(SEQ5_SID.NEXTVAL, SID_I_NEW, crds(i).CODE_I, crds(i).DESC_C, SYSDATE);
END IF;
END;
END IF;
END LOOP;
COMMIT;
END;
UPDATE
我提出了这个看起来很干净的解决方案,但需要大约3秒才能在所有相关表格中插入5000条记录。任何改善这一点的观点都将受到高度赞赏。
create or replace PROCEDURE INSERT_DATA2 (UCMS IN UCMLIST1)
AS
V_IDS KEY_VALUES;
V_UCM KEY_VALUES;
V_ENTITY KEY_VALUES;
V_BR KEY_VALUES;
V_UCMS UCMLIST1;
V_UCMS_VAL UCMLIST1;
V_ENTITIES UCM_ENTITY_LIST1;
V_BRS UCM_ENTITY_BR_LIST1;
BEGIN
-- FIND ALL ID WHICH NEEDS PROCESSING, DISCARD THOSE IDS PROCESSED WITHIN LAST 60 SECONDS
SELECT CASE
WHEN T.ID_I IS NULL
THEN KEY_VALUE(V.ID_I, NULL)
ELSE KEY_VALUE(T.ID_I, NULL)
END
BULK COLLECT INTO V_IDS
FROM T_REF_UCM T
RIGHT JOIN TABLE (UCMS) V ON T.ID_I = V.ID_I
GROUP BY T.ID_I
,V.ID_I
HAVING GET_SECONDS(SYSDATE, MAX(T.LAST_UPDATED_DT)) >= 60
OR T.ID_I IS NULL;
-- Prepare to insert using forall
SELECT UCM1(NULL
,CRD.ID_I
,CRD.VERSION_I
,CRD.ENTITY_IND_STATUS_C
,CRD.IM_IN_SCOPE_FLAG_C
,CRD.VM_IN_SCOPE_FLAG_C
,CRD.IM_START_DATE_DT
,CRD.IM_END_DATE_DT
,CRD.VM_START_DATE_DT
,CRD.VM_END_DATE_DT
,CRD.ERROR_CODE_I
,CRD.ERROR_DESC_C
,CRD.ENTITY)
BULK COLLECT INTO V_UCMS
FROM TABLE (V_IDS) T
INNER JOIN TABLE (UCMS) CRD ON T.ID_I = CRD.ID_I;
-- Insert into T_REF_UCM
FORALL I IN 1 .. V_UCMS.COUNT
INSERT INTO T_REF_UCM (SID_I
,ID_I
,VERSION_I
,ENTITY_IND_STATUS_C
,IM_IN_SCOPE_FLAG_C
,VM_IN_SCOPE_FLAG_C
,IM_START_DATE_DT
,IM_END_DATE_DT
,VM_START_DATE_DT
,VM_END_DATE_DT
,LAST_UPDATED_DT)
VALUES(SEQ_UCM_SID.NEXTVAL
,V_UCMS(I).ID_I
,V_UCMS(I).VERSION_I
,V_UCMS(I).ENTITY_IND_STATUS_C
,V_UCMS(I).IM_IN_SCOPE_FLAG_C
,V_UCMS(I).VM_IN_SCOPE_FLAG_C
,V_UCMS(I).IM_START_DATE_DT
,V_UCMS(I).IM_END_DATE_DT
,V_UCMS(I).VM_START_DATE_DT
,V_UCMS(I).VM_END_DATE_DT
,SYSDATE)
RETURNING KEY_VALUE(ID_I, SID_I)
BULK COLLECT INTO V_UCM;
-- Prepare to insert in validation using forall
SELECT UCM1(T.SID_I
,CRD.ID_I
,CRD.VERSION_I
,CRD.ENTITY_IND_STATUS_C
,CRD.IM_IN_SCOPE_FLAG_C
,CRD.VM_IN_SCOPE_FLAG_C
,CRD.IM_START_DATE_DT
,CRD.IM_END_DATE_DT
,CRD.VM_START_DATE_DT
,CRD.VM_END_DATE_DT
,CRD.ERROR_CODE_I
,CRD.ERROR_DESC_C
,CRD.ENTITY)
BULK COLLECT INTO V_UCMS_VAL
FROM TABLE (V_UCM) T
INNER JOIN TABLE (V_UCMS) CRD ON T.ID_I = CRD.ID_I
WHERE CRD.ERROR_DESC_C IS NOT NULL;
FORALL I IN 1 .. V_UCMS_VAL.COUNT
INSERT INTO T_REF_UCM_VALIDATION (SID_I
,UCM_SID_I
,ERROR_CODE_I
,ERROR_DESC_C
,VERSION_I
,LAST_UPDATED_DT)
VALUES(SEQ_UCM_VALIDATION_SID.NEXTVAL
,V_UCMS_VAL(I).SID_I
,V_UCMS_VAL(I).ERROR_CODE_I
,V_UCMS_VAL(I).ERROR_DESC_C
,V_UCMS_VAL(I).VERSION_I
,SYSDATE);
-- Prepare to insert entity using forall
SELECT UCM_ENTITY1(U.SID_I
,ENT.AMINETID_C
,ENT.ENTITYID_I
,ENT.LEGAL_NAME_C
,ENT.JURISDICTION_C
,ENT.JURIS_EFF_DT
,ENT.IG_EXEMPTION
,ENT.IG_EXEMPTION_START_DT
,ENT.IG_EXEMPTION_END_DT
,ENT.ERROR_CODE_I
,ENT.ERROR_DESC_C
,ENT.BR)
BULK COLLECT INTO V_ENTITIES
FROM TABLE (V_UCM) U
INNER JOIN TABLE (V_UCMS) T ON U.ID_I = T.ID_I
CROSS JOIN TABLE (ENTITY) ENT;
FORALL I IN 1 .. V_ENTITIES.COUNT
INSERT INTO T_REF_UCM_ENTITY (SID_I
,UCM_SID_I
,AMINETID_C
,ENTITYID_I
,LEGAL_NAME_C
,JURISDICTION_C
,JURIS_EFF_DT
,IG_EXEMPTION
,IG_EXEMPTION_START_DT
,IG_EXEMPTION_END_DT
,LAST_UPDATED_DT)
VALUES (SEQ_UCM_ENTITY_SID.NEXTVAL
,V_ENTITIES(I).SID_I
,V_ENTITIES(I).AMINETID_C
,V_ENTITIES(I).ENTITYID_I
,V_ENTITIES(I).LEGAL_NAME_C
,V_ENTITIES(I).JURISDICTION_C
,V_ENTITIES(I).JURIS_EFF_DT
,V_ENTITIES(I).IG_EXEMPTION
,V_ENTITIES(I).IG_EXEMPTION_START_DT
,V_ENTITIES(I).IG_EXEMPTION_END_DT
,SYSDATE)
RETURNING KEY_VALUE(SID_I, UCM_SID_I)
BULK COLLECT INTO V_ENTITY;
-- Prepare to insert BR using forall
SELECT UCM_ENTITY_BR1(E.ID_I
,B.ID_I)
BULK COLLECT INTO V_BRS
FROM TABLE (V_ENTITY) E
INNER JOIN TABLE (V_ENTITIES) T ON E.SID_I = T.SID_I
CROSS JOIN TABLE (BR) B;
FORALL I IN 1 .. V_BRS.COUNT
INSERT INTO T_REF_UCM_ENTITY_BR (SID_I
,ENTITY_SID_I
,BR_ID
,LAST_UPDATED_DT)
VALUES (SEQ_UCM_ENTITY_BR_SID.NEXTVAL
,V_BRS(I).ENTITY_SID_I
,V_BRS(I).ID_I
,SYSDATE);
COMMIT;
END;
答案 0 :(得分:1)
我的第一种方法是进入这个方向(未经测试):
PROCEDURE INSERT_DATA (crds IN CRDSLIST) AS
SID_I_NEW NUMBER;
ENTITY_SID_I_NEW NUMBER;
LAST_DT TIMESTAMP;
ELAPSED_TIME NUMBER;
CURSOR cur_LAST_DT IS
SELECT (SYSDATE - MAX(LAST_UPDATED_DT)) * 24*60*60 AS ELAPSED_TIME, o.*
FROM T_REF1 t
RIGHT OUTER JOIN TABLE(crds) o ON t.ID_I = o.ID_I
GROUP BY t.ID_I;
BEGIN
FOR crd IN cur_LAST_DT LOOP
IF crd.ELAPSED_TIME >= 60 THEN
BEGIN
INSERT INTO T_REF1(SID_I, ID_I, VERSION_I, ENTITY_IND_STATUS_C, LAST_UPDATED_DT)
VALUES(SEQ1_SID.NEXTVAL, crd.ID_I, crd.VERSION_I, crd.ENTITY_IND_STATUS_C, SYSDATE)
RETURNING SID_I INTO SID_I_NEW;
FOR j IN 1 .. crd.ENTITY.COUNT LOOP
INSERT INTO T_REF2(SID_I, UCM_SID_I, ID_C, ID_I, NAME_C) VALUES (SEQ2_SID.NEXTVAL, SID_I_NEW, crd.ENTITY(j).ID_C, crd.ENTITY(j).ID_I, crd.ENTITY(j).NAME_C)
RETURNING SID_I INTO ENTITY_SID_I_NEW;
IF crd.ENTITY(j).BR IS NOT NULL THEN
FOR k IN 1 .. crd.ENTITY(j).BR.COUNT LOOP
INSERT INTO T_REF3(SID_I, ENTITY_SID_I, BR_ID, VERSION_I, LAST_UPDATED_DT) VALUES (SEQ3_SID.NEXTVAL, ENTITY_SID_I_NEW, crd.ENTITY(j).BR(k).ID_I, crd.VERSION_I, SYSDATE);
END LOOP;
END IF;
IF (crd.ENTITY(j).DESC_C IS NOT NULL) THEN
INSERT INTO T_REF2_VALIDATION(SID_I, ENTITY_SID_I, ERROR_CODE_I, ERROR_DESC_C, LAST_UPDATED_DT) VALUES (SEQ4_SID.NEXTVAL, ENTITY_SID_I_NEW, crd.ENTITY(j).CODE_I, crd.ENTITY(j).DESC_C, SYSDATE);
END IF;
END LOOP;
IF (crd.ERROR_DESC_C IS NOT NULL) THEN
INSERT INTO T_REF1_VALIDATION(SID_I, UCM_SID_I, ERROR_CODE_I, ERROR_DESC_C, LAST_UPDATED_DT) VALUES (SEQ5_SID.NEXTVAL, SID_I_NEW, crd.CODE_I, crd.DESC_C, SYSDATE);
END IF;
END;
END IF;
END LOOP;
COMMIT;
END;
但是,您应该尽可能地删除循环。试试这个:
CREATE GLOBAL TEMPORARY TABLE TT_CRDS (CRDS_OBJ CRDS_LIST)
NESTED TABLE CRDS_OBJ STORE AS NT_CRDS_LIST
(NESTED TABLE ENTITY STORE AS NT_ENTITY
(NESTED TABLE BR STORE AS NT_BR)
)
ON COMMIT PRESERVE ROWS;
CREATE OR REPLACE VIEW V_CRDS AS
SELECT *
FROM TT_CRDS
NATURAL JOIN TABLE(CRDS_OBJ) crds;
CREATE OR REPLACE VIEW V_ENTITY AS
SELECT ent.*
FROM TT_CRDS
NATURAL JOIN TABLE(CRDS_OBJ) crds
CROSS JOIN TABLE(ENTITY) ent;
CREATE OR REPLACE VIEW V_BR AS
SELECT ent.*, br.*
FROM TT_CRDS
NATURAL JOIN TABLE(CRDS_OBJ) crds
CROSS JOIN TABLE(ENTITY) ent
CROSS JOIN TABLE(BR) br
PROCEDURE INSERT_DATA (crds IN CRDSLIST) AS
TYPE SID_I_NEW_LIST IS TABLE OF NUMBER;
SID_I_NEW SID_I_NEW_LIST;
ENTITY_SID_I_NEW SID_I_NEW_LIST;
BEGIN
DELETE FROM TT_CRDS;
INSERT INTO TT_CRDS (CRDS_OBJ) VALUES (crds);
INSERT INTO T_REF1(SID_I, ID_I, VERSION_I, ENTITY_IND_STATUS_C, LAST_UPDATED_DT)
SELECT SEQ1_SID.NEXTVAL, crd.ID_I, crd.VERSION_I, crd.ENTITY_IND_STATUS_C, SYSDATE
FROM T_REF1 t
RIGHT OUTER JOIN TABLE(crds) crd ON t.ID_I = crd.ID_I
GROUP BY crd.ID_I, crd.VERSION_I, crd.ENTITY_IND_STATUS_C
HAVING (SYSDATE - MAX(LAST_UPDATED_DT)) >= 60 OR MAX(LAST_UPDATED_DT) IS NULL
RETURNING SID_I BULK COLLECT INTO SID_I_NEW;
INSERT INTO T_REF2 (SID_I, UCM_SID_I, ID_C, ID_I, NAME_C)
SELECT SEQ2_SID.NEXTVAL, SID_I_NEW, crd.ID_C, crd.ID_I, crd.NAME_C
FROM TABLE(crds) crd
CROSS JOIN TABLE(ENTITY) ent
RETURNING SID_I BULK COLLECT INTO ENTITY_SID_I_NEW;
INSERT INTO T_REF3 (SID_I, ENTITY_SID_I, BR_ID, VERSION_I, LAST_UPDATED_DT)
SELECT SEQ3_SID.NEXTVAL, ENTITY_SID_I_NEW, crd.ID_I, crd.VERSION_I, SYSDATE
FROM JOIN TABLE(crds) crd
CROSS JOIN TABLE(ENTITY) ent
CROSS JOIN TABLE(BR) br
WHERE BR IS NOT NULL;
END;
这不是一个有效的解决方案,但它可以让您了解它的外观。临时表和视图不适用于您的生产,但它们将帮助您开发适当的INSERT语句。