КУРСОР ПРОЦЕДУРЫ ХРАНИЛИЩА В ОРАКУЛЕ РАБОТАЕТ ОЧЕНЬ МЕДЛЕННО - PullRequest
0 голосов
/ 18 марта 2019

Одна из моих хранимых процедур недавно заняла около 6 часов, что обычно занимает около 3 часов.При проверке я обнаружил, что курсор требует времени для выполнения.
Обе таблицы присутствуют в моем локальном экземпляре БД.

Мне нужно знать, что может быть причиной этого, и как можно точно настроить процедуру.

Моя хранимая процедура:

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 'DROP TABLE TAB1 PURGE';
COMMIT;

EXECUTE IMMEDIATE 'create table TAB1 Nologging as
select /*+ Parallel(20) */  TRANSACTIONID,TIME_STAMP from OWNER1.log partition('||LOG_D1||') 
where ( MESSAGE = ''WalletUpdate| Request for Estel Update is Processed'' or MESSAGE = ''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);


DECLARE
   CURSOR resp_cur
   IS
        select TRANSACTIONID,to_char(max(TIME_STAMP),'DD-MM-YYYY HH24:MI:SS') TIME_STAMP from TAB1 
        where TRANSACTIONID in (select ORDERREFNUM from TAB2
        where ORDERREFNUM like 'BV%') group by TRANSACTIONID;
BEGIN
   FOR l IN resp_cur
   LOOP
      update TAB2 
      set TCTIME=l.TIME_STAMP 
      where ORDERREFNUM=l.TRANSACTIONID;
      COMMIT;
   END LOOP;
END;

end; 

Ответы [ 3 ]

1 голос
/ 18 марта 2019

Во-первых, DDL имеет неявный коммит, поэтому вам не нужен коммит после удаления таблицы. Во-вторых, почему вы бросаете таблицу и воссоздаете ее, а не просто обрезаете таблицу и вставляете в нее? В-третьих, зачем циклически перемещать курсор, чтобы выполнить обновление, если вы можете сделать это в одном операторе обновления?

Если вам абсолютно необходимо хранить данные в отдельной таблице, я бы переписал вашу процедуру следующим образом:

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.

0 голосов
/ 18 марта 2019

Это неправильный подход: вы обновляете TAB2, сколько раз записи в курсоре resp_cur, я бы переключился на объединение.

0 голосов
/ 18 марта 2019

Хорошо, вашей процедуре нужно много улучшений:

  • В приведенном ниже запросе вы можете использовать user_tab_partitions вместо all_tab_partitions.

    ВЫБРАТЬ имя_раздела в LOG_D1 FROMall_tab_partitions a WHERE table_name = 'LOG' AND TABLE_OWNER = 'OWNER1' и 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);

  • Вы должны включить проверку в таблицу tab1, если она не существует и не требует фиксации здесь, это не оператор DML.

execute немедленный 'DROPTABLE TAB1 PURGE ';COMMIT;

  • Нет необходимости обновлять статистику в процедуре, особенно это касается вновь созданной таблицы и индекса, который уже создан, и только его одного индекса.

Вышеприведенное может немного улучшить производительность, но вы должны проверить, что в журнале таблицы есть индекс для сообщения столбца (но, как я сказал, это неправильное моделирование), а также проверить план запроса на tab2, если это необходимо.индекс.

...