Улучшение производительности plsql с помощью Bulk collect - PullRequest
0 голосов
/ 02 июля 2019

Я использую массовый сбор для улучшения времени выполнения.Когда я не использую массовый сбор, он выполняется за 4 минуты.Но когда я использую массовый сбор, выходных данных нет, и сообщение об ошибке не отображается в консоли.Я вижу созданный пустой файл спула.Пожалуйста, дайте мне знать, если я использовал массовый сбор неправильно, также мы можем использовать это предложение в операторе выбора с лимитом?Таблица состоит максимум из 1 миллиона записей.

SET SERVEROUTPUT ON FORMAT WRAPPED
SET VERIFY OFF
SET FEEDBACK OFF
SET TERMOUT OFF

SPOOL C:\Temp\spool_1.txt

DECLARE

  cursor c2 is (
    select count(distinct e.cdb_pref_event_id)
          ,e.supp_cd
      from (select distinct eh.cdb_customer_id   cdb_customer_id
                           ,eh.cdb_pref_event_id cdb_pref_event_id
                           ,eh.supp_cd           supp_cd
              from (select *
                      from cdb_stg.cpm_pref_event_stg_arc
                     where trunc(load_date) = trunc(sysdate - 1)) eh
              Left outer join cdb_admin.cpm_pref_result er on (eh.cdb_customer_id =
                                                              er.cdb_customer_id and
                                                              eh.cdb_pref_event_id =
                                                              er.cdb_pref_event_id)
             where er.cdb_pref_event_id is null
               and er.cdb_customer_id is null) r
      join cdb_admin.cpm_pref_event_exception e on (r.cdb_customer_id =
                                                   e.cdb_customer_id and
                                                   r.cdb_pref_event_id =
                                                   e.cdb_pref_event_id)
     group by e.supp_cd);

  TYPE totalprefresults is table of NUMBER(20);
  TYPE supcd_1 is table of cdb_admin.cpm_pref_event_stg.supp_cd%TYPE;
  total_prefresults totalprefresults;
  supcd1            supcd_1;
  --Total_prefresults NUMBER(20);
  --SUPCD1 CDB_ADMIN.CPM_PREF_EVENT_STG.supp_cd%TYPE;
  profile_counts NUMBER(20);

  iter Integer := 0;

BEGIN

  select count(distinct cdb_customer_id)
    into profile_counts
    from cdb_admin.cpm_pref_event_exception h
   where cdb_customer_id in
         (Select distinct e.cdb_customer_id
            from (Select distinct eh.cdb_customer_id   cdb_customer_id
                                 ,eh.cdb_pref_event_id cdb_pref_event_id
                                 ,eh.supp_cd           supp_cd
                    from (select *
                            from cdb_stg.cpm_pref_event_stg_arc
                           where trunc(load_date) = trunc(sysdate - 1)) eh
                    Left outer join cdb_admin.cpm_pref_result er on (eh.cdb_customer_id =
                                                                    er.cdb_customer_id and
                                                                    eh.cdb_pref_event_id =
                                                                    er.cdb_pref_event_id)
                   where er.cdb_pref_event_id is null
                     and er.cdb_customer_id is null) r
            join cdb_admin.cpm_pref_event_exception e on (r.cdb_customer_id =
                                                         e.cdb_customer_id and
                                                         r.cdb_pref_event_id =
                                                         e.cdb_pref_event_id)
           where e.supp_cd = 'PROFILE-NOT-FOUND')
     and h.supp_cd != 'PROFILE-NOT-FOUND';

  dbms_output.put_line('TOTAL EVENTS VALIDATION');
  dbms_output.put_line('-------------------------------------------------------------');
  dbms_output.put_line('');

  dbms_output.put_line(rpad('Pref_Counts', 25) || rpad('Supp_CD', 25));

  OPEN c2;
  LOOP
    FETCH c2 BULK COLLECT
      INTO total_prefresults
          ,supcd1 limit 100;
    EXIT WHEN c2%NOTFOUND;
    dbms_output.put_line(rpad(total_prefresults, 25) || rpad(supcd1, 25));

    IF (supcd1 = 'PROFILE-NOT-FOUND')
    then
      dbms_output.put_line('');
      dbms_output.put_line('Profile not found records count : ' ||
                           total_prefresults);

      dbms_output.put_line(profile_counts ||
                           ' : counts moved to other exceptions ');
      dbms_output.put_line((total_prefresults - profile_counts) ||
                           ' : are still in Profile_not_found exception');

    END IF;

    iter := iter + 1;
  END LOOP;
  CLOSE c2;
  dbms_output.put_line('');
  dbms_output.put_line('Number of missing Records: ' || iter);

END;
/
SPOOL OFF

Ответы [ 3 ]

2 голосов
/ 02 июля 2019

Я думаю, что узким местом является это условие: where trunc(load_date) = trunc(sysdate - 1)

У вас есть индекс на trunc(load_date)? Либо создайте индекс на основе функций в trunc(load_date), либо, если у вас уже есть индекс в load_date, попробуйте

WHERE load_date >= trunc(sysdate - 1) AND load_date < trunc(sysdate)

Также проверьте ваши запросы, действительно ли нужно distinct. Удалите их, если это возможно.

0 голосов
/ 02 июля 2019

Массовый сбор может обеспечить значительный прирост производительности. Однако есть пара причин.
Прежде всего, есть разница в значении% notfound. На стандартном курсоре% notfound означает, что все строки уже выбраны, и их больше нет. При массовом сборе этих изменений в 'было недостаточно строк, чтобы достичь указанного ПРЕДЕЛА (если присутствует). Это НЕ означает, что ни одна строка не была выбрана, только указанное ограничение не было достигнуто Например, если ваш лимит равен 100, а извлечение получено только 50, то% notfound вернет True. Это где ссылка руководство терпит неудачу.
Второе - это то, что происходит без предложения limit: все строки из курсора возвращаются в общую память (PGA?). Так в чем же проблема? Если там 100 строк или 1000, то, скорее всего, вы k, но предположим, что есть 100000 или 1M строк, они все еще загружены в память. Наконец (по крайней мере, на данный момент), когда используется предельное предложение, тогда весь процесс Fetch + должен быть сам заключен в цикл, или вы обрабатываете только первую выборку - то есть только указанное предельное количество строк - независимо от того, сколько на самом деле существует. Еще один момент, когда ссылка на руководство не удается. Следующий скелет соответствует вышеуказанному.

    declare 
       max_bulk_rows constant integer  := 1000;  -- define the max number of rows for each fetch ... 

       cursor c_bulk is(
        Select ... ;

       type bulk_row_t is table of c_bulk%rowtype; 
       bulk_row  bulk_row_t; 

    Begin 
       open c_bulk;  
       loop
           fetch c_bulk                      -- fill buffer 
           bulk collect into  bulk_row
           limit max_bulk_row; 

           for i in bulk_row.first .. bulk_row.last -- process each row in buffer 
           loop
               "process individual row here"
           end loop; 

           foreach ...                      -- bulk output of rows here is needed.

           exit when bulk_row.count < max_bulk_row;  -- exit process loop if all rows processed

       end loop ;   -- loop back and fetch next buffer if needed
   close c_bulk;
    ...
   end; 
0 голосов
/ 02 июля 2019

Я перефразировал ваш код, начиная с OPEN c2; до CLOSE c2;

BULK COLLECT должен быть выполнен для сохранения всех данных в коллекции только один раз (за один раз), а затем эта коллекция может бытьиспользовал индекс (т. е. I в следующем случае) в FOR loop следующим образом:

OPEN C2; 
FETCH C2 BULK COLLECT INTO
    TOTAL_PREFRESULTS,
    SUPCD1;
--EXIT WHEN C2%NOTFOUND;
CLOSE C2;

-- To list down all the values before processing the logic
FOR I IN TOTAL_PREFRESULTS.FIRST..TOTAL_PREFRESULTS.LAST LOOP
DBMS_OUTPUT.PUT_LINE(RPAD(TOTAL_PREFRESULTS(I), 25)
                     || RPAD(SUPCD1(I), 25));
END LOOP;


FOR I IN TOTAL_PREFRESULTS.FIRST..TOTAL_PREFRESULTS.LAST LOOP
    IF ( SUPCD1(I) = 'PROFILE-NOT-FOUND' ) THEN
        DBMS_OUTPUT.PUT_LINE('');
        DBMS_OUTPUT.PUT_LINE('Profile not found records count : ' || TOTAL_PREFRESULTS(I));
        DBMS_OUTPUT.PUT_LINE(PROFILE_COUNTS || ' : counts moved to other exceptions ');
        DBMS_OUTPUT.PUT_LINE((TOTAL_PREFRESULTS(I) - PROFILE_COUNTS)
                             || ' : are still in Profile_not_found exception');
    END IF;

    ITER := ITER + 1;
END LOOP;

замените приведенный выше фрагмент кода в своем коде и попытайтесь выполнить.

См. руководство по использованию BULK COLLECT

Cheers !!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...