在程序中更新需要很长时间

时间:2013-03-01 08:33:27

标签: oracle plsql

create or replace procedure Proc_1(P_IN_TABLE_NAME VARCHAR2)
AS
CURSOR T_FACT
IS
SELECT T_ID,T_VER,D_T_ID
from O_T_FACT
where  T_ID is not null
and T_VER is not null;

TYPE call_tab IS TABLE OF O_T_FACT%rowtype;

BEGIN
   IF P_IN_TABLE_NAME ='G_FACT' THEN

   OPEN T_FACT;  
   LOOP 

      EXIT WHEN T_FACT%NOTFOUND ;   

            FETCH T_FACT BULK COLLECT INTO call_data_rec LIMIT no_of_rec;                   
            EXIT WHEN call_data_rec.count = 0;

                       FOR j IN 1..call_data_rec.COUNT
                        loop
                        UPDATE  G_FACT GL set
                        GL.T_ID = call_data_rec(j).T_ID, 
                        GL.T_VER =call_data_rec(j).T_VER,
                        GL.TRANS_FLAG='Y'
                        WHERE GL.G_T_ID = call_data_rec(j).D_T_ID
                        AND GL.T_ID IS NULL 
                        AND GL.T_VER IS NULL;             

                        rec_count := rec_count + 1;                       
                        if mod(rec_count,10000) = 0 then
                        commit;
                        end if;               
                        end loop; 

   end loop;
   CLOSE T_FACT;
   END IF;
End;

这个特殊程序需要很长时间,有没有其他方法可以写这个?这可以在一个更新声明中完成吗?

正如下面所建议的那样,除了给出错误之外,我已经厌倦了 PLS-00436:实施限制:不能引用BULK In-BIND记录表的字段

适用于所有人的新代码

create or replace procedure Proc_update_T_ID(P_IN_TABLE_NAME VARCHAR2)
AS
no_of_rec number := 1000;
CURSOR T_and_V_FACT
IS
SELECT O_T_FACT.T_ID, O_T_FACT.T_VER, O_T_FACT.Downstream_T_ID, G_FACT.rowid row_id,
From O_T_FACT, G_FACT
WHERE O_T_FACT.T_ID IS NOT NULL AND G_FACT.G_T_ID = O_T_FACT.Downstream_T_ID
AND T_VER is not null
AND G_FACT.T_VER IS NULL;

TYPE call_tab IS TABLE OF T_and_V_FACT%rowtype index by binary_integer;
call_data_rec call_tab;

BEGIN
   IF P_IN_TABLE_NAME ='G_FACT' THEN
        IF T_and_V_FACT%ISOPEN THEN
            CLOSE T_and_V_FACT;
        END IF;    

        open T_and_V_FACT;

        LOOP

            FETCH T_and_V_FACT BULK COLLECT
            INTO call_data_rec LIMIT no_of_rec;

            FORALL j IN call_data_rec.FIRST .. call_data_rec.LAST    
            UPDATE  G_FACT GL set
                        GL.T_ID = call_data_rec(j).T_ID, 
                        GL.T_VER =call_data_rec(j).T_VER,
                        GL.TRANS_FLAG='Y'
            WHERE GL.rowid = call_data_rec(j).row_id;      

            COMMIT;                  
            call_data_rec.DELETE;
        EXIT WHEN T_and_V_FACT%NOTFOUND;      
        END LOOP;

        CLOSE T_and_V_FACT;
   End if;

END Proc_1;

2 个答案:

答案 0 :(得分:2)

在我看来,你可以将其重写为如下的单一陈述:

update G_FACT GL
set    (GL.T_ID, GL.T_VER, GL.TRANS_FLAG) = 
            (select T_ID,T_VER, 'Y'
              from   O_T_FACT F
              where  F.T_ID is not null
              and    F.T_VER is not null
              and    GL.G_T_ID = F.D_T_ID)
where  exists (select null
               from   O_T_FACT F
               where  F.T_ID is not null
               and    F.T_VER is not null
               and    GL.G_T_ID = F.D_T_ID)
and    GL.T_ID is null
and    GL.T_VER is null; 

如果这不起作用,那么您应该可以通过将for循环转换为forall语句来获得显着收益:

 FORALL j IN 1..call_data_rec.COUNT
    UPDATE  G_FACT GL set
    GL.T_ID = call_data_rec(j).T_ID, 
    GL.T_VER =call_data_rec(j).T_VER,
    GL.TRANS_FLAG='Y'
    WHERE GL.G_T_ID = call_data_rec(j).D_T_ID
    AND GL.T_ID IS NULL 
    AND GL.T_VER IS NULL;    

此外,重新考虑是否需要循环中的commit。包括这个将:

  • 减慢处理速度
  • 增加击中ORA-1555s的机会
  • 可能会使您的数据处于不一致状态

答案 1 :(得分:1)

我认为你应该改变你的CURSOR并在循环语句中点击UPDATE 此外,我更喜欢使用RowID表示更新语句,使用LIMIT表示游标的获取大小,FOR ALL表示最大性能和内存管理。 我们需要定义一个新类型:

CREATE TYPE my_rec AS OBJECT
  ( T_ID NUMBER
  , T_VER number
  , row_id UROWID)
  );

然后使用 Proc_1 可以提供:

create or replace procedure Proc_1(P_IN_TABLE_NAME VARCHAR2)
AS
no_of_rec number := 1000;
CURSOR T_and_V_FACT
IS
SELECT T_FACT.T_ID, T_FACT.T_VER, T_FACT.D_T_ID, G_FACT.rowid row_id,
  From O_T_FACT, G_FACT
 WHERE T_FACT.T_ID IS NOT NULL AND G_FACT.G_T_ID = O_T_FACT.D_T_ID
   AND T_VER is not null
      -- AND G_FACT.T_ID IS NULL -- not requierd
   AND G_FACT.T_VER IS NULL;

TYPE call_tab IS TABLE OF T_and_V_FACT%rowtype index by binary_integer;
call_data_rec call_tab;

BEGIN
   IF P_IN_TABLE_NAME ='G_FACT' THEN
   IF T_and_V_FACT%ISOPEN THEN
      CLOSE T_and_V_FACT;
   END IF;    
   open T_and_V_FACT;

 LOOP
      FETCH T_and_V_FACT BULK COLLECT
        INTO call_data_rec LIMIT no_of_rec;

      FORALL j IN call_data_rec.FIRST .. call_data_rec.LAST    
       UPDATE  G_FACT GL set
                        GL.T_ID = TREAT(call_data_rec(j) AS my_rec).T_ID, 
                        GL.T_VER =TREAT(call_data_rec(j) AS my_rec).T_VER,
                        GL.TRANS_FLAG='Y'
      WHERE GL.rowid = TREAT(call_data_rec(j) AS my_rec).row_id;      

      COMMIT;                  
      call_data_rec.DELETE;
      EXIT WHEN T_and_V_FACT%NOTFOUND;      
    END LOOP;
    CLOSE T_and_V_FACT;
   End if;
  END Proc_1;

我根据@ Ben的评论编辑了一些部分。
我也根据versions 9i and 10g进行了一些更改 使用TREAT的限制已在11g中删除