Oracle存储的过程游标工作非常缓慢

时间:2019-03-18 08:26:17

标签: oracle performance plsql cursor

我的一个存储过程最近花费了大约6个小时,通常需要大约3个小时才能完成。 检查时,我发现游标正在花时间执行。
这两个表都在我的本地数据库实例中。

我需要知道这可能是什么原因以及如何对程序进行微调。

我的存储过程:

melt

3 个答案:

答案 0 :(得分:1)

首先,DDL具有隐式提交,因此在放置表之后不需要提交。 其次,为什么要删除表并重新创建它,而不仅仅是截断表并插入其中? 第三,当可以在单个update语句中进行更新时,为什么要围绕游标进行更新?

如果您绝对必须将数据存储在单独的表中,我将像这样重写您的过程:

CREATE OR REPLACE PROCEDURE vms_details_d_1 IS
  log_d1 VARCHAR2(20);
BEGIN

  /* IDENTIFY PARTITION */

  SELECT partition_name
  INTO   log_d1
  FROM   all_tab_partitions a
  WHERE  table_name = 'LOG'
  AND    table_owner = 'OWNER1'
  AND    partition_position IN (SELECT MAX(partition_position - 1)
                                FROM   all_tab_partitions b
                                WHERE  table_name = a.table_name
                                AND    a.table_owner = b.table_owner);

  EXECUTE IMMEDIATE 'TRUNCATE TABLE TAB1 reuse storage';

  EXECUTE IMMEDIATE 'insert into TAB1 (transactionid, time_stamp)'||CHR(10)||
                    'select /*+ Parallel(20) */  TRANSACTIONID,TIME_STAMP from OWNER1.log partition(' || log_d1 || ')'||CHR(10)||
                    'where MESSAGE in (''WalletUpdate| Request for Estel Update is Processed'', ''Voucher Core request processed'')';

  EXECUTE IMMEDIATE 'CREATE INDEX IDX_TAB1 on TAB1(TRANSACTIONID)';

  dbms_stats.gather_table_stats(ownname          => 'OWNER2',
                                tabname          => 'TAB1',
                                cascade          => TRUE,
                                estimate_percent => 10,
                                method_opt       => 'for all indexed columns size 1',
                                granularity      => 'ALL',
                                degree           => 1);

  MERGE INTO tab2 tgt
    USING (SELECT transactionid,
                  max(time_stamp) ts
           FROM   tab1
           GROUP BY transactionid) src
      ON (tgt.transactionid = src.transactionid)
  WHEN MATCHED THEN
    UPDATE SET tgt.tctime = to_char(src.ts, 'dd-mm-yyyy hh24:mi:ss'); -- is tab2.tctime really a string? If it's a date, remove the to_char

  COMMIT;
END vms_details_d_1;
/

如果只是复制数据以使其更容易进行更新,则不需要-相反,您可以在单个DML语句中完成所有操作,例如:

CREATE OR REPLACE PROCEDURE vms_details_d_1 IS
  log_d1 VARCHAR2(20);
BEGIN

  /* IDENTIFY PARTITION */

  SELECT partition_name
  INTO   log_d1
  FROM   all_tab_partitions a
  WHERE  table_name = 'LOG'
  AND    table_owner = 'OWNER1'
  AND    partition_position IN (SELECT MAX(partition_position - 1)
                                FROM   all_tab_partitions b
                                WHERE  table_name = a.table_name
                                AND    a.table_owner = b.table_owner);

  EXECUTE IMMEDIATE 'MERGE INTO tab2 tgt'||CHR(10)||
                    '  USING (SELECT transactionid,'||CHR(10)||
                    '                MAX(time_stamp) ts'||CHR(10)||
                    '         FROM   owner1.log partition(' || log_d1 || ')'||CHR(10)||
                    '         GROUP BY transactionid) src'||CHR(10)||
                    '    ON (tgt.transactionid = src.transactionid)'||CHR(10)||
                    'WHEN MATCHED THEN'||CHR(10)||
                    '  UPDATE SET tgt.tctime = to_char(src.ts, ''dd-mm-yyyy hh24:mi:ss'')'; -- is tab2.tctime really a string? If it's a date, remove the to_char

  COMMIT;
END vms_details_d_1;
/

如果知道定义所要分区的谓词,则可以在查询中使用这些谓词,从而无需查找分区名称,因此不需要动态SQL。

答案 1 :(得分:0)

好的,您的程序需要很多增强:

  • 在下面的查询中,您可以使用user_tab_partitions代替all_tab_partitions

    选择partition_name     进入LOG_D1     从all_tab_partitions一个    WHERE table_name ='LOG'      AND TABLE_OWNER ='OWNER1'      和partition_position IN          (SELECT MAX(分区位置-1)             从all_tab_partitions b            WHERE table_name = a.table_name              AND a.table_owner = b.table_owner);

  • 您必须在表tab1上进行检查,以防它不存在并且不需要在此处提交,它不是DML语句。

立即执行“ DROP TABLE TAB1 PURGE”; 承诺;

  • 无需更新过程中的统计信息,尤其是其新创建的表和已经创建的索引以及仅一个索引。

以上内容可能会稍微改善性能,但是您必须检查表日志中是否有用于列消息的索引(但正如我所说的错误建模),还需要检查tab2上的查询计划是否需要索引。

答案 2 :(得分:0)

这是错误的方法,您正在执行的是更新TAB2游标resp_cur中的记录的次数,我将切换到合并。