使用CDT作为输入参数的Oracle过程非常慢

时间:2016-02-10 13:41:31

标签: oracle stored-procedures

我有几个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;

1 个答案:

答案 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语句。