процедура занимает слишком много времени для обновления базы данных - PullRequest
0 голосов
/ 31 марта 2011
CREATE OR REPLACE PROCEDURE UPDATE_CRDT_JV IS
 BEGIN
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT ADJ_DATE FROM ADJUSTMENTS WHERE
         ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'ADJST';
   UPDATE GL_DISTRIBUTION 
         SET GL_DATE = (SELECT PARTY_ADJ_DATE FROM PARTY_ADJUSTMENT
         WHERE PARTY_ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'PRTAJ';
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT VEN_PAY_VOU_DATE FROM PAYMENTS_TO_VENDORS WHERE
         VEN_PAY_VOU_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'CRPAY';
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT CHEQUE_DATE FROM SYS_PAYMENTS_HEADER WHERE
         REF_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'SYSPY';
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT POSTED_DATE FROM PURCHASE_INVOICE_HEADER WHERE
         POSTED_DATE IS NOT NULL AND PIV_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'CRINV';
   UPDATE GL_dISTRIBUTION
     SET GL_dATE = (SELECT DOC_dATE FROM REVERSE_HISTORY
           WHERE TR_NUMBER = TO_NUMBER(GL_DISTRIBUTION.TR_NUMBER)
         AND DOC_dATE IS NOT NULL AND TR_TYPE IN ('SYSPY','CRPAY'))
         WHERE TR_TYPE IN ('RSYSPY','RCRPAY');
   commit;
   UPDATE_INV_DET;
 END;

занимает больше 15 минут, чтобы обновить базу данных. Теперь я обновляю это, используя следующий запрос в SQL PLUS:

EXECUTE UPDATE_CRDT_JV;

Пожалуйста, помогите мне, если кто-нибудь знает решение этой проблемы

Ответы [ 3 ]

4 голосов
/ 31 марта 2011

Я согласен с уже данным советом, чтобы начать выяснять, где твой код тратит время. Тем не менее, ваш случай довольно распространен, и я думаю Я распознаю эту ситуацию: вы закодировали свои операторы обновления таким образом, чтобы к другим таблицам обращались к каждой строке GL_DISTRIBUTION этого типа.

Решение состоит в том, чтобы переписать ваши операторы обновления, и я вижу две возможности сделать это эффективно:

1) Обновить оператор выбора (UPDATE (SELECT ...) SET ... WHERE ...). Это требует наличия некоторых уникальных ключевых ограничений или использования подсказки BYPASS_UJVC.

2) Используйте оператор MERGE.

Ниже вы видите пример того, как переписать ваш код с помощью одного оператора слияния. Я ожидаю значительного прироста производительности, поскольку доступ к другим таблицам теперь выполняется один раз с использованием одного внешнего соединения для каждой из таблиц, а не для каждой строки в таблице GL_DISTRIBUTION.

Пример. Тестовые данные:

SQL> create table gl_distribution (tr_number, tr_type, gl_date)
  2  as
  3  select '1', 'ADJST', date '2011-01-01' from dual union all
  4  select '2', 'ADJST', null from dual union all
  5  select '3', 'PRTAJ', date '2011-01-01' from dual union all
  6  select '4', 'SYSPY', date '2011-01-01' from dual union all
  7  select '5', 'RCRPAY', date '2011-01-01' from dual
  8  /

Table created.

SQL> create table adjustments (adj_number, adj_date)
  2  as
  3  select 1, sysdate from dual union all
  4  select 2, sysdate from dual
  5  /

Table created.

SQL> create table party_adjustment (party_adj_number, party_adj_date)
  2  as
  3  select 3, sysdate from dual union all
  4  select 33, sysdate from dual
  5  /

Table created.

SQL> create table payments_to_vendors (ven_pay_vou_number, ven_pay_vou_date)
  2  as
  3  select 34, sysdate from dual
  4  /

Table created.

SQL> create table sys_payments_header (ref_number,cheque_date)
  2  as
  3  select 4, sysdate from dual
  4  /

Table created.

SQL> create table purchase_invoice_header (piv_number,posted_date)
  2  as
  3  select 35, sysdate from dual
  4  /

Table created.

SQL> create table reverse_history (tr_number,doc_date,tr_type)
  2  as
  3  select 5, sysdate, 'CRPAY' from dual
  4  /

Table created.
SQL>

Ваша процедура (для сравнения):

SQL> CREATE OR REPLACE PROCEDURE UPDATE_CRDT_JV
  2  IS
  3  BEGIN
  4    UPDATE GL_DISTRIBUTION
  5    SET GL_DATE = (SELECT ADJ_DATE FROM ADJUSTMENTS WHERE  ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
  6    WHERE TR_TYPE = 'ADJST'
  7    ;
  8    UPDATE GL_DISTRIBUTION
  9    SET GL_DATE = (SELECT PARTY_ADJ_DATE FROM PARTY_ADJUSTMENT
 10    WHERE PARTY_ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
 11    WHERE TR_TYPE = 'PRTAJ'
 12    ;
 13    UPDATE GL_DISTRIBUTION
 14    SET GL_DATE = (SELECT VEN_PAY_VOU_DATE FROM PAYMENTS_TO_VENDORS
 15    WHERE VEN_PAY_VOU_NUMBER = TO_NUMBER(TR_NUMBER))
 16    WHERE TR_TYPE = 'CRPAY'
 17    ;
 18    UPDATE GL_DISTRIBUTION
 19    SET GL_DATE = (SELECT CHEQUE_DATE FROM SYS_PAYMENTS_HEADER WHERE
 20    REF_NUMBER = TO_NUMBER(TR_NUMBER))
 21    WHERE TR_TYPE = 'SYSPY'
 22    ;
 23    UPDATE GL_DISTRIBUTION
 24    SET GL_DATE = (SELECT POSTED_DATE FROM PURCHASE_INVOICE_HEADER WHERE
 25    POSTED_DATE IS NOT NULL AND PIV_NUMBER = TO_NUMBER(TR_NUMBER))
 26    WHERE TR_TYPE = 'CRINV'
 27    ;
 28    UPDATE GL_dISTRIBUTION
 29    SET GL_dATE = (SELECT DOC_dATE FROM REVERSE_HISTORY
 30    WHERE TR_NUMBER = TO_NUMBER(GL_DISTRIBUTION.TR_NUMBER)
 31    AND DOC_dATE IS NOT NULL AND TR_TYPE IN ('SYSPY','CRPAY'))
 32    WHERE TR_TYPE IN ('RSYSPY','RCRPAY')
 33    ;
 34    --commit;
 35    --UPDATE_INV_DET;
 36  END;
 37  /

Procedure created.

SQL>

Мое предложение:

SQL> create procedure new_update_crdt_jv
  2  as
  3  begin
  4    merge into gl_distribution d
  5    using ( select to_number(d.tr_number) tr_number
  6                 , coalesce
  7                   ( a.adj_date
  8                   , pa.party_adj_date
  9                   , pv.ven_pay_vou_date
 10                   , sph.cheque_date
 11                   , pih.posted_date
 12                   , rh.doc_date
 13                   ) new_date
 14              from gl_distribution d
 15                   left outer join adjustments a
 16                     on to_number(d.tr_number) = a.adj_number
 17                     and d.tr_type = 'ADJST'
 18                   left outer join party_adjustment pa
 19                     on to_number(d.tr_number) = pa.party_adj_number
 20                     and d.tr_type = 'PRTAJ'
 21                   left outer join payments_to_vendors pv
 22                     on to_number(d.tr_number) = pv.ven_pay_vou_number
 23                     and d.tr_type = 'CRPAY'
 24                   left outer join sys_payments_header sph
 25                     on to_number(d.tr_number) = sph.ref_number
 26                     and d.tr_type = 'SYSPY'
 27                   left outer join purchase_invoice_header pih
 28                     on to_number(d.tr_number) = pih.piv_number
 29                     and d.tr_type = 'CRINV'
 30                   left outer join reverse_history rh
 31                     on to_number(d.tr_number) = rh.tr_number
 32                     and rh.tr_type in ('SYSPY','CRPAY')
 33                     and d.tr_type in ('RSYSPY','RCRPAY')
 34          ) n
 35       on ( d.tr_number = n.tr_number)
 36     when matched then
 37          update set d.gl_date = n.new_date
 38    ;
 39  end new_update_crdt_jv;
 40  /

Procedure created.

SQL>

Давайте запустим вашу процедуру:

SQL> select * from gl_distribution
  2  /

T TR_TYP GL_DATE
- ------ -------------------
1 ADJST  01-01-2011 00:00:00
2 ADJST
3 PRTAJ  01-01-2011 00:00:00
4 SYSPY  01-01-2011 00:00:00
5 RCRPAY 01-01-2011 00:00:00

5 rows selected.

SQL> exec update_crdt_jv

PL/SQL procedure successfully completed.

SQL> select * from gl_distribution
  2  /

T TR_TYP GL_DATE
- ------ -------------------
1 ADJST  31-03-2011 14:41:19
2 ADJST  31-03-2011 14:41:19
3 PRTAJ  31-03-2011 14:41:19
4 SYSPY  31-03-2011 14:41:19
5 RCRPAY 31-03-2011 14:41:19

5 rows selected.

SQL> rollback
  2  /

Rollback complete.

SQL>

Моя процедура возвращает те же результаты:

SQL> exec new_update_crdt_jv

PL/SQL procedure successfully completed.

SQL> select * from gl_distribution
  2  /

T TR_TYP GL_DATE
- ------ -------------------
1 ADJST  31-03-2011 14:41:19
2 ADJST  31-03-2011 14:41:19
3 PRTAJ  31-03-2011 14:41:19
4 SYSPY  31-03-2011 14:41:19
5 RCRPAY 31-03-2011 14:41:19

5 rows selected.

Надеюсь, это поможет.

С уважением,
Роб.

3 голосов
/ 31 марта 2011

Решение этой проблемы:

  1. Выясните, где ваш код тратит свое время (т. Е. Профилируйте его)
  2. Узнайте, как ускорить самую медленную часть
  3. Повторяйте до тех пор, пока производительность не станет приемлемой

Если вы предпочитаете догадки, то вы можете попробовать выполнить одно из следующих действий:

Объединить несколько UPDATE в один оператор UPDATE, напримериспользуя условие CASE, как показано @ Aklopper.

Используйте MERGE вместо UPDATE, чтобы избежать коррелированных подзапросов.Может быть лучше, а может и нет.

Посмотрите на процедуру UPDATE_INV_DET, которая вызывается в конце показанной процедуры.

2 голосов
/ 31 марта 2011

Я пришел из среды SQL Server, не поможет ли вам также такой оператор CASE UPDATE (пример использования оператора UPDATE CASE в SQL (не знаю, есть ли у Oracle эквивалентные методы):

   UPDATE titles
           SET GL_DATE=
                     CASE
                       WHEN TR_TYPE = 'ADJST' THEN (SELECT ADJ_DATE FROM ADJUSTMENTS WHERE ADJ_NUMBER = TO_NUMBER(TR_NUMBER) END
                       WHEN TR_TYPE = 'PRTAJ'' THEN (SELECT PARTY_ADJ_DATE FROM PARTY_ADJUSTMENT
         WHERE PARTY_ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
 END
                       ELSE price
                     END
...