oracle Cursor + FORALL需要时间

时间:2019-06-11 07:48:07

标签: oracle performance plsql bulk-operations

我在Oracle中编写了以下脚本,该脚本在一个表中插入了数千行,并使用这些行产生的自动生成的ID并将其用于其他插入。

该脚本可以按预期工作,但问题是需要花费一些时间才能完成。 以下是当前每个表的内容的一些详细信息:

  • 表_0包含16000+行
  • 表_1包含4000多行

使用这些卷,脚本大约需要15到20秒。问题是我打算使用类似的查询来处理数百万行。

以下是脚本调用的函数的代码:

create or replace FUNCTION get_id (name1 IN varchar2) RETURN INTEGER
as res_id INTEGER;
begin

select id  into res_id from table_1 where node_type='type1' and name = 
 name1;

return res_id;
end;
/

这是脚本本身:

DECLARE

  TYPE rt IS RECORD (text1 varchar2(20),text2 varchar(20));

  TYPE texts_tab IS TABLE OF rt;
  TYPE ids_tab   IS TABLE OF table_1.id%TYPE;
  p_texts texts_tab;
  p_ids   ids_tab;
  id_2 integer;
  CURSOR c IS
    SELECT DISTINCT text1,text2 FROM table_0 order by text1,text2;
BEGIN
select FUNC1('type2') into id_2 from dual;
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO p_texts LIMIT 1000;

    FORALL i IN 1 .. p_texts.COUNT
       INSERT INTO table_2(object_id,object_type,parent_id)
        VALUES (SEQ_ID.NEXTVAL, id_2 ,get_id(p_texts(i).text1) ,0,0)
        RETURNING object_id BULK COLLECT INTO p_ids;

    FORALL i IN 1 .. p_ids.COUNT
      insert into table_3 (object_id,field2)
        VALUES ( p_ids(i), p_texts(i).text2 );

     FORALL i IN 1 .. p_ids.COUNT   
        insert into table_1 (node_type,text1,id)
    VALUES('type2', p_texts(i).text1 , p_ids(i));

    EXIT WHEN c%NOTFOUND;
  END LOOP;
  CLOSE c;
  COMMIT;
END;
/

2 个答案:

答案 0 :(得分:2)

我认为您可以使用INSERT ALL来更简单地执行此操作,例如:

DECLARE
  id_2    INTEGER;
BEGIN
  id_2 := func1('type2');

  INSERT ALL
    INTO table_2 (object_id, object_type, parent_id) VALUES (seq_id.nextval, id_2, res_id)
    INTO table_3 (object_id, field2) VALUES (seq_id.nextval, text2)
    INTO table_1 (node_type, text1, ID) VALUES ('type2', text1, seq_id.nextval)
  SELECT t0.text1,
         t0.text2,
         t1.id AS res_id
  FROM   (SELECT DISTINCT text1,
                          text2
          FROM   table_0) t0
         LEFT OUTER JOIN table_1 t1 ON t0.text1 = t1.name AND t1.node_type = 'type1';

  COMMIT;
END;
/

我敲了一个simple test case,以表明生成的序列号已为每个源行的每个目标表重复使用。

如果表之间有外键,则可能需要在插入之前将其禁用,然后再重新启用它们。

通常不建议在INSERT ALL中使用序列(例如https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:9532591900346482312),因此您最好创建一个表以容纳select语句的内容以及序列号,然后在表中使用该新表。全部插入。

答案 1 :(得分:-1)

我删除了函数调用,并将该函数结果合并到直接游标查询中。

实际上,对光标的每一行的函数调用都会降低性能,这就是我的想法。

能否请您尝试以下代码并分享结果:

DECLARE
    TYPE RT IS RECORD (
        TEXT1     VARCHAR2(20),
        TEXT2     VARCHAR(20),
        ID        VARCHAR2(20) -- SET IT ACCORDING TO YOUR DATA TYPE AND SIZE
    );
    TYPE TEXTS_TAB IS
        TABLE OF RT;
    TYPE IDS_TAB IS
        TABLE OF TABLE_1.ID%TYPE;
    P_TEXTS   TEXTS_TAB;
    P_IDS     IDS_TAB;
    ID_2      INTEGER;
    -- CHANGED THIS CURSOR TO REMOVE FUNCTION
    CURSOR C IS
    SELECT DISTINCT
        T0.TEXT1,
        T0.TEXT2,
        T1.ID
    FROM
        TABLE_0 T0,
        TABLE_1 T1
    WHERE
        T1.NODE_TYPE = 'type1'
        AND T1.NAME = T0.TEXT1
    ORDER BY
        TEXT1,
        TEXT2;

BEGIN
    SELECT
        FUNC1('type2')
    INTO ID_2
    FROM
        DUAL;

    OPEN C;
    LOOP
        FETCH C BULK COLLECT INTO P_TEXTS LIMIT 1000;
        FORALL I IN 1..P_TEXTS.COUNT
            INSERT INTO TABLE_2 (
                OBJECT_ID,
                OBJECT_TYPE,
                PARENT_ID
            ) VALUES (
                SEQ_ID.NEXTVAL,
                ID_2,
                P_TEXTS(I).ID, -- ADDED DIRECTLY ID INSTEAD OF FUNCTION CALL
                0,
                0
            ) RETURNING OBJECT_ID BULK COLLECT INTO P_IDS;

        FORALL I IN 1..P_IDS.COUNT
            INSERT INTO TABLE_3 (
                OBJECT_ID,
                FIELD2
            ) VALUES (
                P_IDS(I),
                P_TEXTS(I).TEXT2
            );

        FORALL I IN 1..P_IDS.COUNT
            INSERT INTO TABLE_1 (
                NODE_TYPE,
                TEXT1,
                ID
            ) VALUES (
                'type2',
                P_TEXTS(I).TEXT1,
                P_IDS(I)
            );

        EXIT WHEN C%NOTFOUND;
    END LOOP;

    CLOSE C;
    COMMIT;
END;
/